[Vm-dev] Tasks of the heartbeat code

Holger Freyther holger at freyther.de
Fri Dec 25 20:40:21 UTC 2015


> On 14 Sep 2015, at 22:43, Eliot Miranda <eliot.miranda at gmail.com> wrote:
> 
> Hi Holger,


Dear Eliot,

time flies but I finally made a bit of progress.



> You've read what John has said.  I think the easiest thing to do is not to stop and start the heartbeat thread, but merely to set a flag when entering ioRelinquishProcessorForMicroseconds, clearing it on exit, and have the heartbeat test this flag before doing anything that could disturb the VM thread(s).  The overhead of the heartbeat thread is very low.  It doesn't call select or anything like that.  All it does is spin, calling nanosleep, polling the clock and then setting a variable.  For example, avoiding polling the clock when in ioRelinquishProcessorForMicroseconds might save a few cycles.

I have done my current experiments in uild.linux32x86/squeak.cog.v3/build.itimerheartbeat. I don't know if that
is the closest that the pharovm is using but the issue is that it creates work when the system would be idle and
as such "cost" (either your cloud platform charges you for CPU usage, or it takes resources from your cluster,
etc).

I have done it using the X11 plugin:

1.) Don't disable SIGIO for the X11 handle:

@@ -6723,7 +6775,7 @@
 	  aioHandle(browserPipes[0], npHandler, AIO_RX);
 	}
       isConnectedToXServer= 1;
-      aioEnable(stXfd, 0, AIO_EXT);
+      aioEnable(stXfd, 0, 0);
       aioHandle(stXfd, xHandler, AIO_RX);
     }
   return 0;

2.) Hack (needs to use CAS or such) ioProcessEvents. So we remember when a
SIGIO has been received and instead of just trying to poll every nextPollUsecs we
poll if we know it will succeed.

--- ../../src/vm/gcc3x-cointerp.c	(revision 3546)
+++ ../../src/vm/gcc3x-cointerp.c	(working copy)
@@ -39583,6 +39583,7 @@
 }
 
 
+int doDispatch = 0;
 /*	Check for possible interrupts and handle one if necessary.
 	Answer if a context switch has occurred. */
 
@@ -39661,15 +39662,13 @@
 			switched = 1;
 		}
 	}
-	if (((now = ioUTCMicroseconds())) >= GIV(nextPollUsecs)) {
+
+	if (doDispatch) {
+		doDispatch = 0;
 		GIV(statIOProcessEvents) += 1;
 		ioProcessEvents();
-
-		/* msecs to wait before next call to ioProcessEvents.  Note that strictly
-		   speaking we might need to update 'now' at this point since
-		   ioProcessEvents could take a very long time on some platforms */
-		GIV(nextPollUsecs) = now + 20000;
 	}
+	now = ioUTCMicroseconds();
 
 	if (GIV(interruptPending)) {
 


--- vm/aio.c	(revision 3546)
+++ vm/aio.c	(working copy)
@@ -143,10 +143,17 @@
 #endif
 
 /* initialise asynchronous i/o */
+void sigio_signal(int sig)
+{
+  extern void forceInterruptCheck(int);	/* not really, but hey */
+  extern int doDispatch;
+  doDispatch = 1;
+  forceInterruptCheck(sig);
+}
 
+
 void aioInit(void)
 {
-  extern void forceInterruptCheck(int);	/* not really, but hey */
 
   FD_ZERO(&fdMask);
   FD_ZERO(&rdMask);
@@ -155,7 +162,7 @@
   FD_ZERO(&xdMask);
   maxFd= 0;
   signal(SIGPIPE, SIG_IGN);
-  signal(SIGIO,   forceInterruptCheck);
+  signal(SIGIO,   sigio_signal);
 }


3.) When the relinquish primitive is called 

--- vm-display-X11/sqUnixX11.c	(revision 3546)
+++ vm-display-X11/sqUnixX11.c	(working copy)
@@ -4828,8 +4828,38 @@
 
 static sqInt display_ioRelinquishProcessorForMicroseconds(sqInt microSeconds)
 {
-  aioSleepForUsecs(handleEvents() ? 0 : microSeconds);
+  sigset_t set;
+  sigemptyset(&set);
+
+  extern usqLong getNextWakeupUsecs();
+  if (getNextWakeupUsecs() == 0) {
+  	sigsuspend(&set);
+  } else {
+	usqLong nextWakeUp = getNextWakeupUsecs();
+	usqLong now = ioUTCMicroseconds();
+	struct itimerval value;
+
+	sqLong deltaMicro = nextWakeUp - now;
+	if (deltaMicro < 1)
+		deltaMicro = 1;
+	/* don't re-run automatically */
+	value.it_interval.tv_sec = value.it_interval.tv_usec = 0;
+	value.it_value.tv_sec = deltaMicro / 1000000;
+	value.it_value.tv_usec = deltaMicro % 1000000;
+	setitimer(ITIMER_REAL, &value, &value);
+	sigsuspend(&set);
+	value.it_value = value.it_interval;
+	setitimer(ITIMER_REAL, &value, NULL);
+
+	extern int doDispatch;
+	if (doDispatch) {
+		doDispatch = 0;
+		aioPoll(0);
+	}
+  }
+
   return 0;
+#endif
 }


let me follow up with the "result" of my test.

holger


More information about the Vm-dev mailing list