Porting Smalltalk

In order to have a live programming environment on Haiku, I tried to port opensmalltalk-vm.

Here are the changes made so far:

diff --git a/platforms/unix/plugins/SocketPlugin/sqUnixSocket.c b/platforms/unix/plugins/SocketPlugin/sqUnixSocket.c
index df8a1eec0..22f3cae87 100644
--- a/platforms/unix/plugins/SocketPlugin/sqUnixSocket.c
+++ b/platforms/unix/plugins/SocketPlugin/sqUnixSocket.c
@@ -57,6 +57,10 @@
 #endif
 #endif

+#ifdef __HAIKU__
+#define IFF_RUNNING IFF_LINK
+#endif
+
 #ifdef ACORN
 # include <time.h>
 # define __time_t
diff --git a/platforms/unix/vm/aio.c b/platforms/unix/vm/aio.c
index 1f7153fbc..cac95691d 100644
--- a/platforms/unix/vm/aio.c
+++ b/platforms/unix/vm/aio.c
@@ -88,6 +88,10 @@
   # define signal(a, b) sigset(a, b)
 # endif

+# if __HAIKU__
+  # define SIGIO SIGPOLL
+# endif
+
 #else /* !HAVE_CONFIG_H -- assume lowest common demoninator */

 # include <errno.h>
diff --git a/platforms/unix/vm/include_ucontext.h b/platforms/unix/vm/include_ucontext.h
index 3fb578445..63521f942 100644
--- a/platforms/unix/vm/include_ucontext.h
+++ b/platforms/unix/vm/include_ucontext.h
@@ -16,6 +16,8 @@
 #endif
 #ifdef __OpenBSD__
 # include <sys/signal.h>
+#elif __HAIKU__
+# include <signal.h>
 #elif __sun
 /* Single UNIX Specification (SUS), Version 2 specifies <ucontext.h> */
 # include <ucontext.h>
@@ -63,6 +65,14 @@
 # define _PC_IN_UCONTEXT uc_mcontext.gregs[EIP]
 # define _FP_IN_UCONTEXT uc_mcontext.gregs[REG_FP]
 # define _SP_IN_UCONTEXT uc_mcontext.gregs[REG_SP]
+#elif __HAIKU__ && __i386__
+# define _PC_IN_UCONTEXT uc_mcontext.eip
+# define _FP_IN_UCONTEXT uc_mcontext.ebp
+# define _SP_IN_UCONTEXT uc_mcontext.esp
+#elif __HAIKU__ && __amd64__
+# define _PC_IN_UCONTEXT uc_mcontext.rip
+# define _FP_IN_UCONTEXT uc_mcontext.rbp
+# define _SP_IN_UCONTEXT uc_mcontext.rsp
 #elif __linux__ && __i386__
 # define _PC_IN_UCONTEXT uc_mcontext.gregs[REG_EIP]
 # define _FP_IN_UCONTEXT uc_mcontext.gregs[REG_EBP]

This changes allow to build the bare VM. But in order to display graphics, it requires a display plugin, which uses X11 and OpenGL. I tried to build it using xlibe, but the display plugin requires X11/extensions/Xrandr.h which xlibe lacks.

There are two ways to solve this problem:

  • Either one needs to add Xrandr to xlibe
  • Or to implement a display plugin which uses native Haiku API

But I have no experience with neither X11 nor Be API. So I would appreciate some help or at least suggestions.

5 Likes

If you have to learn something, I vote to go native.

DO you know about BeSqueak the SmallTalk Squeak for BeOS? The source is on BeShare.
I’ll also share it on Haiku’s Telegram

2 Likes

Reading the source code, it appears Xrandr is optional; it compiles against it but can fall back on other things at runtime if it’s not present. So, you should be able to add some modifications to allow compiling without it, either.

I won’t be too surprised if you run into other missing Xlibe functionality, though, after you get it to build. Let me know if you do and I’ll take a look.

1 Like

It also seems to require Xatom.

Xatom.h is a standard header from xorgproto, it should be present?

1 Like

And here it is:

I had to disable OpenGL support because there’s no GL/glx.h. Here’s the patch for opensmalltalk vm: haiku.patch · GitHub.

12 Likes

For some reason network does not work in the vm, and an attempt to connect to somewhere freezes the vm for a while. I do not know how to debug this.

The Smalltalk VM? Or are you using Haiku inside a VM?

If it’s the Smalltalk VM, you can just attach a debugger…

Parse out glx.h for gl.h where needed. Review configure/build for X11/GLX specific handling to convert to non-GLX OS setups.

Smalltalk VM.

If it’s the Smalltalk VM, you can just attach a debugger…

Indeed. But a VM is probably harder to debug than a regular program.

With GDB I finally know where VM stalls. Here, inside recv call:

/* answer 1 if the given socket is readable,
		  0 if read would block, or
		 -1 if the socket is no longer connected */

static int
socketReadable(int s)
{
  char buf[1];
  int n = recv(s, (void *)buf, 1, MSG_PEEK);
  if (n > 0) return 1;
  if ((n < 0) && (errno == EWOULDBLOCK)) return 0;
  return -1;	/* EOF */
}

It seems like this function is not supposed to block and yet it blocks. I don’t know why yet.

Haiku’s activity report mentions that MSG_PEEK could cause a hang:

waddlesplash (in a series of commits scattered across the month) refactored the handling of the flags parameter of send/sendmsg and recv/recvmsg inside the network stack. Initially, this was done to fix test failures and hangs in some programs using UNIX domain sockets which passed flags that altered behavior (like MSG_PEEK) but which were not actually handled by the UNIX domain sockets implementation; the initial change just made EOPNOTSUPP be returned when unhandled flags were specified. This, however, wound up uncovering that some applications (including curl) had been passing some (relatively unimportant) unsupported flags, leading to some of those being first implemented in the UNIX domain sockets module, but eventually led to refactoring in the main network sockets module and a variety of flags logic being consolidated there rather than implemented uniquely by every socket module (TCP, UDP, etc.) A number of minor bugs in the implementation of such flags were corrected along the way, along with various code cleanups done to the socket module and others (old FIFO templates code, etc.)

If the socket is supposed to be non-blocking, you’ll have to go back to where it was set up that way. and figure out what went wrong.

If it’s supposed to block, except for the recv() in socketReadable(), then you might try MSG_PEEK|MSG_DONTWAIT.

This seems funky to me. If it can be made to work, no real problem, but if it continues to give you trouble, it might be worth the time to look into how this is normally done with poll() or select().

I looked a bit more. Networking code seems to call aioEnable quite a bit, which in turn invokes this function:

static void
makeFileDescriptorNonBlockingAndSetupSigio(int fd) {
	int	arg;

#if defined(O_ASYNC)
	if (fcntl(fd, F_SETOWN, getpid()) < 0)
		perror("fcntl(F_SETOWN, getpid())");
	if ((arg = fcntl(fd, F_GETFL, 0)) < 0)
		perror("fcntl(F_GETFL)");
	if (fcntl(fd, F_SETFL, arg | O_NONBLOCK | O_ASYNC) < 0)
		perror("fcntl(F_SETFL, O_ASYNC)");
# if defined(F_SETNOSIGPIPE)
	if ((arg = fcntl(fd, F_SETNOSIGPIPE, 1)) < 0)
		perror("fcntl(F_GETFL)");
# endif
	FPRINTF((stderr, "makeFileDescriptorNonBlockingAndSetupSigio(%d): Elicit SIGIO via O_ASYNC/fcntl\n", fd));

#elif defined(FASYNC)
	if (fcntl(fd, F_SETOWN, getpid()) < 0)
		perror("fcntl(F_SETOWN, getpid())");
	if ((arg = fcntl(fd, F_GETFL, 0)) < 0)
		perror("fcntl(F_GETFL)");
	if (fcntl(fd, F_SETFL, arg | O_NONBLOCK | FASYNC) < 0)
		perror("fcntl(F_SETFL, FASYNC)");
# if defined(F_SETNOSIGPIPE)
	if ((arg = fcntl(fd, F_SETNOSIGPIPE, 1)) < 0)
		perror("fcntl(F_SETNOSIGPIPE)");
# endif
	FPRINTF((stderr, "makeFileDescriptorNonBlockingAndSetupSigio(%d): Elicit SIGIO via FASYNC/fcntl\n", fd));

#elif defined(FIOASYNC)
	arg = getpid();
	if (ioctl(fd, SIOCSPGRP, &arg) < 0)
		perror("ioctl(SIOCSPGRP, getpid())");
	arg = 1;
	if (ioctl(fd, FIOASYNC, &arg) < 0)
		perror("ioctl(FIOASYNC, 1)");
	FPRINTF((stderr, "makeFileDescriptorNonBlockingAndSetupSigio(%d): Elicit SIGIO via FIOASYNC/fcntl\n", fd));
#else
	FPRINTF((stderr, "makeFileDescriptorNonBlockingAndSetupSigio(%d): UNABLE TO ELICIT SIGIO!!\n", fd));
#endif
}

If Haiku defines neither O_ASYNC nor FASYNC or FIOASYNC, then this function is a no-op and O_NONBLOCK is not set.

All of those options are related to POSIX AIO (which sends signals to processes when I/O is available), which we indeed do not support. Does this VM really depend on POSIX AIO? If it doesn’t, then it should just set O_NONBLOCK without O_ASYNC.

Yes, it seems to require either epoll or aio for asynchronous IO.

If Haiku has neither epoll nor POSIX AIO, what should I use instead as an alternative?

You can use either kqueue (as in FreeBSD) or plain old poll or select. kqueue would be the closest one.