[Vm-dev] [commit] r2528 - Load function pointer early in threaded FFI checking sequence, for compatibility

commits at squeakvm.org commits at squeakvm.org
Thu Feb 2 02:03:35 UTC 2012


Author: eliot
Date: 2012-02-01 18:03:33 -0800 (Wed, 01 Feb 2012)
New Revision: 2528

Added:
   branches/Cog/platforms/unix/vm/sqUnixITimerHeartbeat.c
   branches/Cog/platforms/unix/vm/sqUnixITimerTickerHeartbeat.c
Modified:
   branches/Cog/platforms/Cross/vm/sqSCCSVersion.h
   branches/Cog/platforms/Cross/vm/sqTicker.c
   branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDev.c
   branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDevMousePS2.c
   branches/Cog/platforms/unix/vm/sqUnixHeartbeat.c
   branches/Cog/src/plugins/SqueakFFIPrims/SqueakFFIPrims.c
   branches/Cog/unixbuild/bld/mvm
   branches/Cog/unixbuild/mtbld/mvm
Log:
Load function pointer early in threaded FFI checking sequence, for compatibility
with old FFIPlugin.
Make Qwaq ticker support optional, requiring VM_TICKER to enable.  This to
simplify ticker for older linuxes.
Include Michael Zeder's fixes to revive vm-display-fbdev build on linux.
Avoid assuming readlink supports -f flag on linux builds.



Property changes on: branches/Cog/platforms/Cross/vm/sqSCCSVersion.h
___________________________________________________________________
Modified: checkindate
   - Mon Dec 12 11:21:45 PST 2011
   + Wed Feb  1 18:00:12 PST 2012

Modified: branches/Cog/platforms/Cross/vm/sqTicker.c
===================================================================
--- branches/Cog/platforms/Cross/vm/sqTicker.c	2012-01-23 19:01:42 UTC (rev 2527)
+++ branches/Cog/platforms/Cross/vm/sqTicker.c	2012-02-02 02:03:33 UTC (rev 2528)
@@ -28,6 +28,24 @@
  *   DEALINGS IN THE SOFTWARE.
  */
 
+#if !VM_TICKER
+/* stubs for (unsupported) high-priority ticker support */
+# include "sq.h"
+
+void
+addSynchronousTickee(void (*tickee)(void), unsigned periodms, unsigned roundms)
+{ error("ticker unsupported in this VM"); }
+
+void
+addHighPriorityTickee(void (*tickee)(void), unsigned periodms)
+{ error("ticker unsupported in this VM"); }
+
+void
+checkHighPriorityTickees(usqLong utcMicrosecondClock) {}
+
+void
+ioSynchronousCheckForEvents() {}
+#else /* VM_TICKER */
 /* High-priority and synchronous tickee function support.
  *
  * Tickers provide the ability to register a tickee function that will be
@@ -249,3 +267,4 @@
 	unblockVMThreadAfterYieldToHighPriorityTickerThread();
 #endif
 }
+#endif /* VM_TICKER */

Modified: branches/Cog/platforms/unix/vm/sqUnixHeartbeat.c
===================================================================
--- branches/Cog/platforms/unix/vm/sqUnixHeartbeat.c	2012-01-23 19:01:42 UTC (rev 2527)
+++ branches/Cog/platforms/unix/vm/sqUnixHeartbeat.c	2012-02-02 02:03:33 UTC (rev 2528)
@@ -1,28 +1,33 @@
 /****************************************************************************
-*   PROJECT: Unix (pthread or setitimer) heartbeat logic for Stack VM
+*   PROJECT: Unix (pthread) heartbeat logic for Stack/Cog VM
 *   FILE:    sqUnixHeartbeat.c
 *   CONTENT: 
 *
 *   AUTHOR:  Eliot Miranda
 *   ADDRESS: 
-*   EMAIL:   eliot at qwaq.com
+*   EMAIL:   eliot.miranda at gmail.com
 *   RCSID:   $Id$
 *
 *   NOTES: 
+*  Feb   1st, 2012, EEM refactored into three separate files.
 *  July 31st, 2008, EEM added heart-beat thread.
 *  Aug  20th, 2009, EEM added 64-bit microsecond clock support code
 *
 *****************************************************************************/
 
+#if ITIMER_HEARTBEAT
+# if VM_TICKER
+#   include "sqUnixITimerTickerHeartbeat.c"
+# else
+#   include "sqUnixITimerHeartbeat.c"
+# endif
+#else /* ITIMER_HEARTBEAT */
+
 #include "sq.h"
 #include "sqAssert.h"
 #include "sqMemoryFence.h"
 #include <errno.h>
-#if ITIMER_HEARTBEAT
-# include <signal.h>
-#else
-# include <pthread.h>
-#endif
+#include <pthread.h>
 #include <sys/types.h>
 #include <sys/time.h>
 
@@ -310,289 +315,18 @@
 	}
 	else
 		heartbeats += 1;
-#if ITIMER_HEARTBEAT
-	/* While we use SA_RESTART to ensure system calls are restarted, this is
-	 * not universally effective.  In particular, connect calls can abort if
-	 * a system call is made in the signal handler, i.e. the pthread_kill in
-	 * prodHighPriorityThread.  So we avoid this if possible by not prodding
-	 * the high-priority thread unless there are high-priority tickees as
-	 * indicated by numAsyncTickees > 0.
-	 */
-	if (numAsyncTickees > 0) {
-		void prodHighPriorityThread(void);
-		prodHighPriorityThread();
-	}
-#else
 	checkHighPriorityTickees(utcMicrosecondClock);
-#endif
 	forceInterruptCheckFromHeartbeat();
 
 	errno = saved_errno;
 }
 
-#if ITIMER_HEARTBEAT
-	/* Hack for linux server to avoid the thread priority issue, i.e. that
-	 * linux doesn't provide priorities for SCHED_OTHER and won't let a non-
-	 * superuser process set the scheduling policy to anything else).
-	 *
-	 * Solution is to drive heartbeat from an interval timer instead of a high-
-	 * priority thread blocking in a sleep.  We use ITIMER_REAL/SIGALRM (see
-	 * below).  setitimer(2) claims max itimer resolution on 2.6.13 is 4
-	 * milliseconds, but on 2.6.18-128.el5 one can see periods of 1.2ms.
-	 *
-	 * The high-priority tickees cannot be run from the interrupt-driven heart-
-	 * beat and must be run from a separate thread to avoid numerous sources
-	 * of deadlock (e.g. the lock in malloc).  But since the thread has the
-	 * same priority as the VM thread we arrange that the VM yields to the
-	 * high-priority ticker when it is running.  This is co-ordinated in
-	 * sqTicker.c by ioSynchronousCheckForEvents (the synchronous ticker)
-	 * yielding if requested by checkHighPriorityTickees.  To perform the yield,
-	 * these functions use yieldToHighPriorityTickerThread and
-	 * unblockVMThreadAfterYieldToHighPriorityTickerThread to do the dirty work.
-	 *
-	 * The itimer signal handler ensures it is running on the VM thread and
-	 * then invokes a signal handler on the high-priority thread (see
-	 * prodHighPriorityThread).  This signal breaks the high-priority thread
-	 * out of its nanosleep and it calls checkHighPriorityTickees.
-	 */
-#define TICKER_SIGNAL SIGUSR2 /* SIGURSR1 dumps the stack */
-static pthread_t tickerThread;
-
-void
-prodHighPriorityThread()
-{
-	/* invoke the tickerThread's signal handler */
-	pthread_kill(tickerThread, TICKER_SIGNAL);
-}
-
-static void
-high_performance_tick_handler(int sig, struct siginfo *sig_info, void *context)
-{
-static int tickCheckInProgress;
-
-	if (tickCheckInProgress) return;
-
-	tickCheckInProgress = 1;
-	checkHighPriorityTickees(ioUTCMicroseconds());
-	tickCheckInProgress = 0;
-}
-
-static void *
-tickerSleepCycle(void *ignored)
-{
-	struct timespec naptime;
-
-	naptime.tv_sec = 3600;
-	naptime.tv_nsec = 0;
-
-	while (1)
-		(void)nanosleep(&naptime, 0);
-	return 0;
-}
-
-/* We require the error check because we're lazy in preventing multiple
- * attempts at locking yield_mutex in yieldToHighPriorityTickerThread.
- */
-#if defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP)
-# define THE_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
-#elif defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
-# define THE_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
-#else
-# define THE_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
-#endif
-static pthread_mutex_t yield_sync = THE_MUTEX_INITIALIZER;
-static pthread_mutex_t yield_mutex = THE_MUTEX_INITIALIZER;
-static pthread_cond_t yield_cond = PTHREAD_COND_INITIALIZER;
-
-/* Private to sqTicker.c ioSynchronousCheckForEvents */
-void
-yieldToHighPriorityTickerThread()
-{
-	int err;
-
-	if ((err = pthread_mutex_lock(&yield_mutex))) {
-		if (err != EDEADLK)
-			fprintf(stderr,"pthread_mutex_lock yield_mutex %s\n", strerror(err));
-	}
-	/* If lock fails then unblockVMThreadAfterYieldToHighPriorityTickerThread
-	 * has locked and we should not block.
-	 */
-	if ((err = pthread_mutex_lock(&yield_sync))) {
-		if (err != EDEADLK)
-			fprintf(stderr,"pthread_mutex_lock yield_sync %s\n", strerror(err));
-	}
-	else if ((err = pthread_cond_wait(&yield_cond, &yield_mutex)))
-		fprintf(stderr,"pthread_cond_wait %s\n", strerror(err));
-}
-
-/* Private to sqTicker.c checkHighPriorityTickees */
-void
-unblockVMThreadAfterYieldToHighPriorityTickerThread()
-{
-	/* If yield_sync is already locked the VM thread is very likely blocking in
-	 * yieldToHighPriorityTickerThread and so yield_cond should be signalled.
-	 */
-	if (pthread_mutex_trylock(&yield_sync) == 0) /* success */
-		pthread_mutex_unlock(&yield_sync);
-	else
-		pthread_cond_signal(&yield_cond);
-}
-
-
-#if !defined(DEFAULT_BEAT_MS)
-# define DEFAULT_BEAT_MS 2
-#endif
-static int beatMilliseconds = DEFAULT_BEAT_MS;
-
-/* Use ITIMER_REAL/SIGALRM because the VM can enter a sleep in the OS via
- * e.g. ioRelinquishProcessorForMicroseconds in which the OS will assume the
- * process is not running and not deliver the signals.
- */
-#if 0
-# define THE_ITIMER ITIMER_PROF
-# define ITIMER_SIGNAL SIGPROF
-#elif 0
-# define THE_ITIMER ITIMER_VIRTUAL
-# define ITIMER_SIGNAL SIGVTALRM
-#else
-# define THE_ITIMER ITIMER_REAL
-# define ITIMER_SIGNAL SIGALRM
-#endif
-
-/* With ticker support it may be that a ticker function invoked heartbeat takes
- * so long that another timer interrupt occurs before heartbeat has finished.
- * The absence of SA_NODEFER in heartbeat_handler_action.sa_flags prevents
- * reentrancy, if available.
- *
- * With lots of threads it may be that the kernel delivers the signal on some
- * other thread.
- */
-#if !defined(SA_NODEFER)
-static int handling_heartbeat = 0;
-#endif
-
-static void
-heartbeat_handler(int sig, struct siginfo *sig_info, void *context)
-{
-	if (!ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) {
-		pthread_kill(getVMOSThread(),sig);
-		return;
-	}
-
-#if !defined(SA_NODEFER)
-  {	int zeroAndPreviousHandlingHeartbeat = 0;
-    sqCompareAndSwap(handling_heartbeat,zeroAndPreviousHandlingHeartbeat,1);
-	if (zeroAndPreviousHandlingHeartbeat)
-		return;
-  }
-
-	handling_heartbeat = 1;
-#endif
-
-	heartbeat();
-
-#if 0
-	if (heartbeats % 250 == 0) {
-		printf(".");
-		fflush(stdout);
-	}
-#endif
-#if !defined(SA_NODEFER)
-	handling_heartbeat = 0;
-#endif
-}
-
-#define NEED_SIGALTSTACK 1 /* for safety; some time need to turn off and test */
-#if NEED_SIGALTSTACK
-/* If the ticker is run from the heartbeat signal handler one needs to use an
- * alternative stack to avoid overflowing the VM's stack pages.  Keep
- * the structure around for reference during debugging.
- */
-#define SIGNAL_STACK_SIZE (1024 * sizeof(void *) * 16)
-static stack_t signal_stack;
-#endif /* NEED_SIGALTSTACK */
-
-void
-ioInitHeartbeat()
-{
-extern sqInt suppressHeartbeatFlag;
-	int er;
-	struct timespec halfAMo;
-	struct sigaction heartbeat_handler_action, ticker_handler_action;
-	struct itimerval pulse;
-
-	if (suppressHeartbeatFlag) return;
-
-#if NEED_SIGALTSTACK
-	signal_stack.ss_flags = 0;
-	signal_stack.ss_size = SIGNAL_STACK_SIZE;
-	if (!(signal_stack.ss_sp = malloc(signal_stack.ss_size))) {
-		perror("ioInitHeartbeat malloc");
-		exit(1);
-	}
-	if (sigaltstack(&signal_stack, 0) < 0) {
-		perror("ioInitHeartbeat sigaltstack");
-		exit(1);
-	}
-#endif /* NEED_SIGALTSTACK */
-
-	halfAMo.tv_sec  = 0;
-	halfAMo.tv_nsec = 1000 * 100;
-	if ((er= pthread_create(&tickerThread,
-							(const pthread_attr_t *)0,
-							tickerSleepCycle,
-							0))) {
-		errno = er;
-		perror("beat thread creation failed");
-		exit(errno);
-	}
-
-	ticker_handler_action.sa_sigaction = high_performance_tick_handler;
-	/* N.B. We _do not_ include SA_NODEFER to specifically prevent reentrancy
-	 * during the heartbeat. We /must/ include SA_RESTART to avoid issues with
-     * e.g. ODBC connections.
-	 */
-	ticker_handler_action.sa_flags = SA_RESTART | SA_ONSTACK;
-	sigemptyset(&ticker_handler_action.sa_mask);
-	if (sigaction(TICKER_SIGNAL, &ticker_handler_action, 0)) {
-		perror("ioInitHeartbeat sigaction");
-		exit(1);
-	}
-
-	heartbeat_handler_action.sa_sigaction = heartbeat_handler;
-	/* N.B. We _do not_ include SA_NODEFER to specifically prevent reentrancy
-	 * during the heartbeat.  We *must* include SA_RESTART to avoid breaking
-	 * lots of external code (e.g. the mysql odbc connect).
-	 */
-	heartbeat_handler_action.sa_flags = SA_RESTART | SA_ONSTACK;
-	sigemptyset(&heartbeat_handler_action.sa_mask);
-	if (sigaction(ITIMER_SIGNAL, &heartbeat_handler_action, 0)) {
-		perror("ioInitHeartbeat sigaction");
-		exit(1);
-	}
-
-	pulse.it_interval.tv_sec = beatMilliseconds / 1000;
-	pulse.it_interval.tv_usec = (beatMilliseconds % 1000) * 1000;
-	pulse.it_value = pulse.it_interval;
-	if (setitimer(THE_ITIMER, &pulse, &pulse)) {
-		perror("ioInitHeartbeat setitimer");
-		exit(1);
-	}
-}
-
-void
-ioSetHeartbeatMilliseconds(int ms)
-{
-	beatMilliseconds = ms;
-	ioInitHeartbeat();
-}
-#else /* ITIMER_HEARTBEAT */
 typedef enum { dead, condemned, nascent, quiescent, active } machine_state;
 
 static int					stateMachinePolicy;
 static struct sched_param	stateMachinePriority;
 
-static machine_state beatState = nascent;
+static volatile machine_state beatState = nascent;
 
 #if !defined(DEFAULT_BEAT_MS)
 # define DEFAULT_BEAT_MS 2
@@ -669,7 +403,6 @@
 	beatperiod.tv_sec = beatMilliseconds / 1000;
 	beatperiod.tv_nsec = (beatMilliseconds % 1000) * 1000 * 1000;
 }
-#endif /* ITIMER_HEARTBEAT */
 
 int
 ioHeartbeatMilliseconds() { return beatMilliseconds; }
@@ -690,3 +423,4 @@
 	}
 	return frequency;
 }
+#endif /* ITIMER_HEARTBEAT */

Added: branches/Cog/platforms/unix/vm/sqUnixITimerHeartbeat.c
===================================================================
--- branches/Cog/platforms/unix/vm/sqUnixITimerHeartbeat.c	                        (rev 0)
+++ branches/Cog/platforms/unix/vm/sqUnixITimerHeartbeat.c	2012-02-02 02:03:33 UTC (rev 2528)
@@ -0,0 +1,444 @@
+/****************************************************************************
+*   PROJECT: Unix (setitimer) heartbeat logic for Stack/Cog VM *without* ticker
+*   FILE:    sqUnixHeartbeat.c
+*   CONTENT: 
+*
+*   AUTHOR:  Eliot Miranda
+*   ADDRESS: 
+*   EMAIL:   eliot.miranda at gmail.com
+*   RCSID:   $Id$
+*
+*   NOTES: 
+*  Feb   1st, 2012, EEM refactored into three separate files.
+*  July 31st, 2008, EEM added heart-beat thread.
+*  Aug  20th, 2009, EEM added 64-bit microsecond clock support code
+*
+*****************************************************************************/
+
+#include "sq.h"
+#include "sqAssert.h"
+#include "sqMemoryFence.h"
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#define SecondsFrom1901To1970      2177452800ULL
+#define MicrosecondsFrom1901To1970 2177452800000000ULL
+
+#define MicrosecondsPerSecond 1000000ULL
+#define MillisecondsPerSecond 1000ULL
+
+#define MicrosecondsPerMillisecond 1000ULL
+
+static unsigned volatile long long utcMicrosecondClock;
+static unsigned volatile long long localMicrosecondClock;
+static unsigned volatile long millisecondClock; /* for the ioMSecs clock. */
+static unsigned long long utcStartMicroseconds; /* for the ioMSecs clock. */
+static long long vmGMTOffset = 0;
+static unsigned long long frequencyMeasureStart = 0;
+static unsigned long heartbeats;
+
+#define microToMilliseconds(usecs) ((((usecs) - utcStartMicroseconds) \
+									/ MicrosecondsPerMillisecond) \
+									& MillisecondClockMask)
+
+#define LOG_CLOCK 1
+
+#if LOG_CLOCK
+# define LOGSIZE 1024
+static unsigned long long useclog[LOGSIZE];
+static unsigned long mseclog[LOGSIZE];
+static int logClock = 0;
+static unsigned int ulogidx = (unsigned int)-1;
+static unsigned int mlogidx = (unsigned int)-1;
+# define logusecs(usecs) do { sqLowLevelMFence(); \
+							if (logClock) useclog[++ulogidx % LOGSIZE] = (usecs); \
+						} while (0)
+# define logmsecs(msecs) do { sqLowLevelMFence(); \
+							if (logClock) mseclog[++mlogidx % LOGSIZE] = (msecs); \
+						} while (0)
+void
+ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *runInNOutp, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip)
+{
+	logClock = *runInNOutp;
+	sqLowLevelMFence();
+	*runInNOutp = LOGSIZE;
+	*usecsp = useclog;
+	*uip = ulogidx % LOGSIZE;
+	*msecsp = mseclog;
+	*mip = mlogidx % LOGSIZE;
+}
+#else /* LOG_CLOCK */
+# define logusecs(usecs) 0
+# define logmsecs(msecs) 0
+void
+ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *np, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip)
+{
+	*np = *uip = *mip = 0;
+	*usecsp = *msecsp = 0;
+}
+#endif /* LOG_CLOCK */
+
+/* Compute the current VM time basis, the number of microseconds from 1901. */
+
+static unsigned long long
+currentUTCMicroseconds()
+{
+	struct timeval utcNow;
+
+	gettimeofday(&utcNow,0);
+	return ((utcNow.tv_sec * MicrosecondsPerSecond) + utcNow.tv_usec)
+			+ MicrosecondsFrom1901To1970;
+}
+
+/*
+ * Update the utc and local microsecond clocks, and the millisecond clock.
+ * Since this is invoked from interupt code, and since the clocks are 64-bit values
+ * that are read concurrently by the VM, care must be taken to access these values
+ * atomically on 32-bit systems.  If they are not accessed atomically there is a
+ * possibility of fetching the two halves of the clock from different ticks which
+ * would cause a jump in the clock of 2^32 microseconds (1 hr, 11 mins, 34 secs).
+ *
+ * Since an interrupt could occur between any two instructions the clock must be
+ * read atomically as well as written atomically.  If possible this can be
+ * implemented without locks using atomic 64-bit reads and writes.
+ */
+
+#include "sqAtomicOps.h"
+
+static void
+updateMicrosecondClock()
+{
+	unsigned long long newUtcMicrosecondClock;
+	unsigned long long newLocalMicrosecondClock;
+
+	newUtcMicrosecondClock = currentUTCMicroseconds();
+
+	/* The native clock may go backwards, e.g. due to NTP adjustments, although
+	 * why it can't avoid small backward steps itself, I don't know.  Simply
+	 * ignore backward steps and wait until the clock catches up again.  Of
+	 * course this will cause problems if the clock is manually adjusted.  To
+	 * which the doctor says, "don't do that".
+	 */
+	if (!asserta(newUtcMicrosecondClock >= utcMicrosecondClock)) {
+		logusecs(0); /* if logging log a backward step as 0 */
+		return;
+	}
+	newLocalMicrosecondClock = newUtcMicrosecondClock + vmGMTOffset;
+
+	set64(utcMicrosecondClock,newUtcMicrosecondClock);
+	set64(localMicrosecondClock,newLocalMicrosecondClock);
+	millisecondClock = microToMilliseconds(newUtcMicrosecondClock);
+
+	logusecs(newUtcMicrosecondClock);
+	logmsecs(millisecondClock);
+}
+
+void
+ioUpdateVMTimezone()
+{
+	time_t utctt;
+	updateMicrosecondClock();
+	utctt = (get64(utcMicrosecondClock) - MicrosecondsFrom1901To1970)
+				/ MicrosecondsPerSecond;
+	vmGMTOffset = localtime(&utctt)->tm_gmtoff * MicrosecondsPerSecond;
+}
+
+sqLong
+ioHighResClock(void)
+{
+  /* return the value of the high performance counter */
+  sqLong value = 0;
+#if defined(__GNUC__) && ( defined(i386) || defined(__i386) || defined(__i386__)  \
+			|| defined(i486) || defined(__i486) || defined (__i486__) \
+			|| defined(intel) || defined(x86) || defined(i86pc) )
+    __asm__ __volatile__ ("rdtsc" : "=A"(value));
+#else
+# error "no high res clock defined"
+#endif
+  return value;
+}
+
+#if !macintoshSqueak
+static unsigned int   lowResMSecs= 0;
+static struct timeval startUpTime;
+
+/*
+ * Answer the millisecond clock as computed on Unix prior to the 64-bit
+ * microsecond clock.  This is to help verify that the new clock is correct.
+ */
+sqInt
+ioOldMSecs(void)
+{
+  struct timeval now;
+  unsigned int nowMSecs;
+
+#if 1 /* HAVE_HIGHRES_COUNTER */
+
+  /* if we have a cheap, high-res counter use that to limit
+     the frequency of calls to gettimeofday to something reasonable. */
+  static unsigned int baseMSecs = 0;      /* msecs when we took base tick */
+  static sqLong baseTicks = 0;/* base tick for adjustment */
+  static sqLong tickDelta = 0;/* ticks / msec */
+  static sqLong nextTick = 0; /* next tick to check gettimeofday */
+
+  sqLong thisTick = ioHighResClock();
+
+  if(thisTick < nextTick) return lowResMSecs;
+
+#endif
+
+  gettimeofday(&now, 0);
+  if ((now.tv_usec-= startUpTime.tv_usec) < 0)
+    {
+      now.tv_usec+= 1000000;
+      now.tv_sec-= 1;
+    }
+  now.tv_sec-= startUpTime.tv_sec;
+  nowMSecs = (now.tv_usec / 1000 + now.tv_sec * 1000);
+
+#if 1 /* HAVE_HIGHRES_COUNTER */
+  {
+    unsigned int msecsDelta;
+    /* Adjust our rdtsc rate every 10...100 msecs as needed.
+       This also covers msecs clock-wraparound. */
+    msecsDelta = nowMSecs - baseMSecs;
+    if(msecsDelta < 0 || msecsDelta > 100) {
+      /* Either we've hit a clock-wraparound or we are being
+	 sampled in intervals larger than 100msecs.
+	 Don't try any fancy adjustments */
+      baseMSecs = nowMSecs;
+      baseTicks = thisTick;
+      nextTick = 0;
+      tickDelta = 0;
+    } else if(msecsDelta >= 10) {
+      /* limit the rate of adjustments to 10msecs */
+      baseMSecs = nowMSecs;
+      tickDelta = (thisTick - baseTicks) / msecsDelta;
+      nextTick = baseTicks = thisTick;
+    }
+    nextTick += tickDelta;
+  }
+#endif
+  return lowResMSecs= nowMSecs;
+}
+#endif /* !macintoshSqueak */
+
+usqLong
+ioUTCMicroseconds() { return get64(utcMicrosecondClock); }
+
+usqLong
+ioLocalMicroseconds() { return get64(localMicrosecondClock); }
+
+/* This is an expensive interface for use by profiling code that wants the time
+ * now rather than as of the last heartbeat.
+ */
+usqLong
+ioUTCMicrosecondsNow() { return currentUTCMicroseconds(); }
+
+int
+ioMSecs() { return millisecondClock; }
+
+/* Note: ioMicroMSecs returns *milli*seconds */
+int ioMicroMSecs(void) { return microToMilliseconds(currentUTCMicroseconds()); }
+
+/* returns the local wall clock time */
+int
+ioSeconds(void) { return get64(localMicrosecondClock) / MicrosecondsPerSecond; }
+
+int
+ioUTCSeconds(void) { return get64(utcMicrosecondClock) / MicrosecondsPerSecond; }
+
+/*
+ * On Mac OS X use the following.
+ * On Unix use dpy->ioRelinquishProcessorForMicroseconds
+ */
+#if macintoshSqueak
+int
+ioRelinquishProcessorForMicroseconds(int microSeconds)
+{
+    long	realTimeToWait;
+	extern usqLong getNextWakeupUsecs();
+	usqLong nextWakeupUsecs = getNextWakeupUsecs();
+	usqLong utcNow = get64(utcMicrosecondClock);
+
+    if (nextWakeupUsecs <= utcNow) {
+		/* if nextWakeupUsecs is non-zero the next wakeup time has already
+		 * passed and we should not wait.
+		 */
+        if (nextWakeupUsecs != 0)
+			return 0;
+		realTimeToWait = microSeconds;
+    }
+    else {
+        realTimeToWait = nextWakeupUsecs - utcNow;
+		if (realTimeToWait > microSeconds)
+			realTimeToWait = microSeconds;
+	}
+
+	aioSleepForUsecs(realTimeToWait);
+
+	return 0;
+}
+#endif /* !macintoshSqueak */
+
+void
+ioInitTime(void)
+{
+	ioUpdateVMTimezone(); /* does updateMicrosecondClock as a side-effect */
+	updateMicrosecondClock(); /* this can now compute localUTCMicroseconds */
+	utcStartMicroseconds = utcMicrosecondClock;
+#if !macintoshSqueak
+	/* This is only needed for ioOldMSecs */
+	gettimeofday(&startUpTime, 0);
+#endif
+}
+
+static void
+heartbeat()
+{
+	int saved_errno = errno;
+
+	updateMicrosecondClock();
+	if (get64(frequencyMeasureStart) == 0) {
+		set64(frequencyMeasureStart,utcMicrosecondClock);
+		heartbeats = 0;
+	}
+	else
+		heartbeats += 1;
+	forceInterruptCheckFromHeartbeat();
+
+	errno = saved_errno;
+}
+
+#if !defined(DEFAULT_BEAT_MS)
+# define DEFAULT_BEAT_MS 2
+#endif
+static int beatMilliseconds = DEFAULT_BEAT_MS;
+
+/* Use ITIMER_REAL/SIGALRM because the VM can enter a sleep in the OS via
+ * e.g. ioRelinquishProcessorForMicroseconds in which the OS will assume the
+ * process is not running and not deliver the signals.
+ */
+#if 0
+# define THE_ITIMER ITIMER_PROF
+# define ITIMER_SIGNAL SIGPROF
+#elif 0
+# define THE_ITIMER ITIMER_VIRTUAL
+# define ITIMER_SIGNAL SIGVTALRM
+#else
+# define THE_ITIMER ITIMER_REAL
+# define ITIMER_SIGNAL SIGALRM
+#endif
+
+#if !defined(SA_NODEFER)
+static int handling_heartbeat = 0;
+#endif
+
+static void
+heartbeat_handler(int sig, struct siginfo *sig_info, void *context)
+{
+#if !defined(SA_NODEFER)
+  {	int zeroAndPreviousHandlingHeartbeat = 0;
+    sqCompareAndSwap(handling_heartbeat,zeroAndPreviousHandlingHeartbeat,1);
+	if (zeroAndPreviousHandlingHeartbeat)
+		return;
+  }
+
+	handling_heartbeat = 1;
+#endif
+
+	heartbeat();
+
+#if 0
+	if (heartbeats % 250 == 0) {
+		printf(".");
+		fflush(stdout);
+	}
+#endif
+#if !defined(SA_NODEFER)
+	handling_heartbeat = 0;
+#endif
+}
+
+#define NEED_SIGALTSTACK 1 /* for safety; some time need to turn off and test */
+#if NEED_SIGALTSTACK
+/* If the ticker is run from the heartbeat signal handler one needs to use an
+ * alternative stack to avoid overflowing the VM's stack pages.  Keep
+ * the structure around for reference during debugging.
+ */
+#define SIGNAL_STACK_SIZE (1024 * sizeof(void *) * 16)
+static stack_t signal_stack;
+#endif /* NEED_SIGALTSTACK */
+
+void
+ioInitHeartbeat()
+{
+extern sqInt suppressHeartbeatFlag;
+	int er;
+	struct sigaction heartbeat_handler_action;
+	struct itimerval pulse;
+
+	if (suppressHeartbeatFlag) return;
+
+#if NEED_SIGALTSTACK
+	signal_stack.ss_flags = 0;
+	signal_stack.ss_size = SIGNAL_STACK_SIZE;
+	if (!(signal_stack.ss_sp = malloc(signal_stack.ss_size))) {
+		perror("ioInitHeartbeat malloc");
+		exit(1);
+	}
+	if (sigaltstack(&signal_stack, 0) < 0) {
+		perror("ioInitHeartbeat sigaltstack");
+		exit(1);
+	}
+#endif /* NEED_SIGALTSTACK */
+
+	heartbeat_handler_action.sa_sigaction = heartbeat_handler;
+	/* N.B. We _do not_ include SA_NODEFER to specifically prevent reentrancy
+	 * during the heartbeat.  We *must* include SA_RESTART to avoid breaking
+	 * lots of external code (e.g. the mysql odbc connect).
+	 */
+	heartbeat_handler_action.sa_flags = SA_RESTART | SA_ONSTACK;
+	sigemptyset(&heartbeat_handler_action.sa_mask);
+	if (sigaction(ITIMER_SIGNAL, &heartbeat_handler_action, 0)) {
+		perror("ioInitHeartbeat sigaction");
+		exit(1);
+	}
+
+	pulse.it_interval.tv_sec = beatMilliseconds / 1000;
+	pulse.it_interval.tv_usec = (beatMilliseconds % 1000) * 1000;
+	pulse.it_value = pulse.it_interval;
+	if (setitimer(THE_ITIMER, &pulse, &pulse)) {
+		perror("ioInitHeartbeat setitimer");
+		exit(1);
+	}
+}
+
+void
+ioSetHeartbeatMilliseconds(int ms)
+{
+	beatMilliseconds = ms;
+	ioInitHeartbeat();
+}
+
+int
+ioHeartbeatMilliseconds() { return beatMilliseconds; }
+
+
+/* Answer the average heartbeats per second since the stats were last reset.
+ */
+unsigned long
+ioHeartbeatFrequency(int resetStats)
+{
+	unsigned duration = (ioUTCMicroseconds() - get64(frequencyMeasureStart))
+						/ MicrosecondsPerSecond;
+	unsigned frequency = duration ? heartbeats / duration : 0;
+
+	if (resetStats) {
+		unsigned long long zero = 0;
+		set64(frequencyMeasureStart,zero);
+	}
+	return frequency;
+}

Added: branches/Cog/platforms/unix/vm/sqUnixITimerTickerHeartbeat.c
===================================================================
--- branches/Cog/platforms/unix/vm/sqUnixITimerTickerHeartbeat.c	                        (rev 0)
+++ branches/Cog/platforms/unix/vm/sqUnixITimerTickerHeartbeat.c	2012-02-02 02:03:33 UTC (rev 2528)
@@ -0,0 +1,610 @@
+/****************************************************************************
+*   PROJECT: Unix (setitimer) heartbeat logic for Stack/Cog VM with ticker
+*            implemented using hack for linux systems that don't support
+*            thread priorities.
+*   FILE:    sqUnixITimerTickerHeartbeat.c
+*   CONTENT: 
+*
+*   AUTHOR:  Eliot Miranda
+*   ADDRESS: 
+*   EMAIL:   eliot.miranda at gmail.com
+*   RCSID:   $Id$
+*
+*   NOTES: 
+*  Feb   1st, 2012, EEM refactored into three separate files.
+*  July 31st, 2008, EEM added heart-beat thread.
+*  Aug  20th, 2009, EEM added 64-bit microsecond clock support code
+*
+*****************************************************************************/
+
+#include "sq.h"
+#include "sqAssert.h"
+#include "sqMemoryFence.h"
+#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#define SecondsFrom1901To1970      2177452800ULL
+#define MicrosecondsFrom1901To1970 2177452800000000ULL
+
+#define MicrosecondsPerSecond 1000000ULL
+#define MillisecondsPerSecond 1000ULL
+
+#define MicrosecondsPerMillisecond 1000ULL
+
+static unsigned volatile long long utcMicrosecondClock;
+static unsigned volatile long long localMicrosecondClock;
+static unsigned volatile long millisecondClock; /* for the ioMSecs clock. */
+static unsigned long long utcStartMicroseconds; /* for the ioMSecs clock. */
+static long long vmGMTOffset = 0;
+static unsigned long long frequencyMeasureStart = 0;
+static unsigned long heartbeats;
+
+#define microToMilliseconds(usecs) ((((usecs) - utcStartMicroseconds) \
+									/ MicrosecondsPerMillisecond) \
+									& MillisecondClockMask)
+
+#define LOG_CLOCK 1
+
+#if LOG_CLOCK
+# define LOGSIZE 1024
+static unsigned long long useclog[LOGSIZE];
+static unsigned long mseclog[LOGSIZE];
+static int logClock = 0;
+static unsigned int ulogidx = (unsigned int)-1;
+static unsigned int mlogidx = (unsigned int)-1;
+# define logusecs(usecs) do { sqLowLevelMFence(); \
+							if (logClock) useclog[++ulogidx % LOGSIZE] = (usecs); \
+						} while (0)
+# define logmsecs(msecs) do { sqLowLevelMFence(); \
+							if (logClock) mseclog[++mlogidx % LOGSIZE] = (msecs); \
+						} while (0)
+void
+ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *runInNOutp, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip)
+{
+	logClock = *runInNOutp;
+	sqLowLevelMFence();
+	*runInNOutp = LOGSIZE;
+	*usecsp = useclog;
+	*uip = ulogidx % LOGSIZE;
+	*msecsp = mseclog;
+	*mip = mlogidx % LOGSIZE;
+}
+#else /* LOG_CLOCK */
+# define logusecs(usecs) 0
+# define logmsecs(msecs) 0
+void
+ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *np, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip)
+{
+	*np = *uip = *mip = 0;
+	*usecsp = *msecsp = 0;
+}
+#endif /* LOG_CLOCK */
+
+/* Compute the current VM time basis, the number of microseconds from 1901. */
+
+static unsigned long long
+currentUTCMicroseconds()
+{
+	struct timeval utcNow;
+
+	gettimeofday(&utcNow,0);
+	return ((utcNow.tv_sec * MicrosecondsPerSecond) + utcNow.tv_usec)
+			+ MicrosecondsFrom1901To1970;
+}
+
+/*
+ * Update the utc and local microsecond clocks, and the millisecond clock.
+ * Since this is invoked from interupt code, and since the clocks are 64-bit values
+ * that are read concurrently by the VM, care must be taken to access these values
+ * atomically on 32-bit systems.  If they are not accessed atomically there is a
+ * possibility of fetching the two halves of the clock from different ticks which
+ * would cause a jump in the clock of 2^32 microseconds (1 hr, 11 mins, 34 secs).
+ *
+ * Since an interrupt could occur between any two instructions the clock must be
+ * read atomically as well as written atomically.  If possible this can be
+ * implemented without locks using atomic 64-bit reads and writes.
+ */
+
+#include "sqAtomicOps.h"
+
+static void
+updateMicrosecondClock()
+{
+	unsigned long long newUtcMicrosecondClock;
+	unsigned long long newLocalMicrosecondClock;
+
+	newUtcMicrosecondClock = currentUTCMicroseconds();
+
+	/* The native clock may go backwards, e.g. due to NTP adjustments, although
+	 * why it can't avoid small backward steps itself, I don't know.  Simply
+	 * ignore backward steps and wait until the clock catches up again.  Of
+	 * course this will cause problems if the clock is manually adjusted.  To
+	 * which the doctor says, "don't do that".
+	 */
+	if (!asserta(newUtcMicrosecondClock >= utcMicrosecondClock)) {
+		logusecs(0); /* if logging log a backward step as 0 */
+		return;
+	}
+	newLocalMicrosecondClock = newUtcMicrosecondClock + vmGMTOffset;
+
+	set64(utcMicrosecondClock,newUtcMicrosecondClock);
+	set64(localMicrosecondClock,newLocalMicrosecondClock);
+	millisecondClock = microToMilliseconds(newUtcMicrosecondClock);
+
+	logusecs(newUtcMicrosecondClock);
+	logmsecs(millisecondClock);
+}
+
+void
+ioUpdateVMTimezone()
+{
+	time_t utctt;
+	updateMicrosecondClock();
+	utctt = (get64(utcMicrosecondClock) - MicrosecondsFrom1901To1970)
+				/ MicrosecondsPerSecond;
+	vmGMTOffset = localtime(&utctt)->tm_gmtoff * MicrosecondsPerSecond;
+}
+
+sqLong
+ioHighResClock(void)
+{
+  /* return the value of the high performance counter */
+  sqLong value = 0;
+#if defined(__GNUC__) && ( defined(i386) || defined(__i386) || defined(__i386__)  \
+			|| defined(i486) || defined(__i486) || defined (__i486__) \
+			|| defined(intel) || defined(x86) || defined(i86pc) )
+    __asm__ __volatile__ ("rdtsc" : "=A"(value));
+#else
+# error "no high res clock defined"
+#endif
+  return value;
+}
+
+#if !macintoshSqueak
+static unsigned int   lowResMSecs= 0;
+static struct timeval startUpTime;
+
+/*
+ * Answer the millisecond clock as computed on Unix prior to the 64-bit
+ * microsecond clock.  This is to help verify that the new clock is correct.
+ */
+sqInt
+ioOldMSecs(void)
+{
+  struct timeval now;
+  unsigned int nowMSecs;
+
+#if 1 /* HAVE_HIGHRES_COUNTER */
+
+  /* if we have a cheap, high-res counter use that to limit
+     the frequency of calls to gettimeofday to something reasonable. */
+  static unsigned int baseMSecs = 0;      /* msecs when we took base tick */
+  static sqLong baseTicks = 0;/* base tick for adjustment */
+  static sqLong tickDelta = 0;/* ticks / msec */
+  static sqLong nextTick = 0; /* next tick to check gettimeofday */
+
+  sqLong thisTick = ioHighResClock();
+
+  if(thisTick < nextTick) return lowResMSecs;
+
+#endif
+
+  gettimeofday(&now, 0);
+  if ((now.tv_usec-= startUpTime.tv_usec) < 0)
+    {
+      now.tv_usec+= 1000000;
+      now.tv_sec-= 1;
+    }
+  now.tv_sec-= startUpTime.tv_sec;
+  nowMSecs = (now.tv_usec / 1000 + now.tv_sec * 1000);
+
+#if 1 /* HAVE_HIGHRES_COUNTER */
+  {
+    unsigned int msecsDelta;
+    /* Adjust our rdtsc rate every 10...100 msecs as needed.
+       This also covers msecs clock-wraparound. */
+    msecsDelta = nowMSecs - baseMSecs;
+    if(msecsDelta < 0 || msecsDelta > 100) {
+      /* Either we've hit a clock-wraparound or we are being
+	 sampled in intervals larger than 100msecs.
+	 Don't try any fancy adjustments */
+      baseMSecs = nowMSecs;
+      baseTicks = thisTick;
+      nextTick = 0;
+      tickDelta = 0;
+    } else if(msecsDelta >= 10) {
+      /* limit the rate of adjustments to 10msecs */
+      baseMSecs = nowMSecs;
+      tickDelta = (thisTick - baseTicks) / msecsDelta;
+      nextTick = baseTicks = thisTick;
+    }
+    nextTick += tickDelta;
+  }
+#endif
+  return lowResMSecs= nowMSecs;
+}
+#endif /* !macintoshSqueak */
+
+usqLong
+ioUTCMicroseconds() { return get64(utcMicrosecondClock); }
+
+usqLong
+ioLocalMicroseconds() { return get64(localMicrosecondClock); }
+
+/* This is an expensive interface for use by profiling code that wants the time
+ * now rather than as of the last heartbeat.
+ */
+usqLong
+ioUTCMicrosecondsNow() { return currentUTCMicroseconds(); }
+
+int
+ioMSecs() { return millisecondClock; }
+
+/* Note: ioMicroMSecs returns *milli*seconds */
+int ioMicroMSecs(void) { return microToMilliseconds(currentUTCMicroseconds()); }
+
+/* returns the local wall clock time */
+int
+ioSeconds(void) { return get64(localMicrosecondClock) / MicrosecondsPerSecond; }
+
+int
+ioUTCSeconds(void) { return get64(utcMicrosecondClock) / MicrosecondsPerSecond; }
+
+/*
+ * On Mac OS X use the following.
+ * On Unix use dpy->ioRelinquishProcessorForMicroseconds
+ */
+#if macintoshSqueak
+int
+ioRelinquishProcessorForMicroseconds(int microSeconds)
+{
+    long	realTimeToWait;
+	extern usqLong getNextWakeupUsecs();
+	usqLong nextWakeupUsecs = getNextWakeupUsecs();
+	usqLong utcNow = get64(utcMicrosecondClock);
+
+    if (nextWakeupUsecs <= utcNow) {
+		/* if nextWakeupUsecs is non-zero the next wakeup time has already
+		 * passed and we should not wait.
+		 */
+        if (nextWakeupUsecs != 0)
+			return 0;
+		realTimeToWait = microSeconds;
+    }
+    else {
+        realTimeToWait = nextWakeupUsecs - utcNow;
+		if (realTimeToWait > microSeconds)
+			realTimeToWait = microSeconds;
+	}
+
+	aioSleepForUsecs(realTimeToWait);
+
+	return 0;
+}
+#endif /* !macintoshSqueak */
+
+void
+ioInitTime(void)
+{
+	ioUpdateVMTimezone(); /* does updateMicrosecondClock as a side-effect */
+	updateMicrosecondClock(); /* this can now compute localUTCMicroseconds */
+	utcStartMicroseconds = utcMicrosecondClock;
+#if !macintoshSqueak
+	/* This is only needed for ioOldMSecs */
+	gettimeofday(&startUpTime, 0);
+#endif
+}
+
+static void
+heartbeat()
+{
+	int saved_errno = errno;
+
+	updateMicrosecondClock();
+	if (get64(frequencyMeasureStart) == 0) {
+		set64(frequencyMeasureStart,utcMicrosecondClock);
+		heartbeats = 0;
+	}
+	else
+		heartbeats += 1;
+
+	/* While we use SA_RESTART to ensure system calls are restarted, this is
+	 * not universally effective.  In particular, connect calls can abort if
+	 * a system call is made in the signal handler, i.e. the pthread_kill in
+	 * prodHighPriorityThread.  So we avoid this if possible by not prodding
+	 * the high-priority thread unless there are high-priority tickees as
+	 * indicated by numAsyncTickees > 0.
+	 */
+	if (numAsyncTickees > 0) {
+		void prodHighPriorityThread(void);
+		prodHighPriorityThread();
+	}
+	forceInterruptCheckFromHeartbeat();
+
+	errno = saved_errno;
+}
+
+/* Hack for linux server to avoid the thread priority issue, i.e. that
+ * linux doesn't provide priorities for SCHED_OTHER and won't let a non-
+ * superuser process set the scheduling policy to anything else).
+ *
+ * Solution is to drive heartbeat from an interval timer instead of a high-
+ * priority thread blocking in a sleep.  We use ITIMER_REAL/SIGALRM (see
+ * below).  setitimer(2) claims max itimer resolution on 2.6.13 is 4
+ * milliseconds, but on 2.6.18-128.el5 one can see periods of 1.2ms.
+ *
+ * The high-priority tickees cannot be run from the interrupt-driven heartbeat
+ * and must be run from a separate thread to avoid numerous sources of deadlock
+ * (e.g. the lock in malloc).  But since the thread has the same priority as the
+ * VM thread we arrange that the VM yields to the high-priority ticker when it's
+ * running.  This is co-ordinated in sqTicker.c by ioSynchronousCheckForEvents
+ * (the synchronous ticker) yielding if requested by checkHighPriorityTickees.
+ * To perform the yield, these functions use yieldToHighPriorityTickerThread and
+ * unblockVMThreadAfterYieldToHighPriorityTickerThread to do the dirty work.
+ *
+ * The itimer signal handler ensures it is running on the VM thread and
+ * then invokes a signal handler on the high-priority thread (see
+ * prodHighPriorityThread).  This signal breaks the high-priority thread
+ * out of its nanosleep and it calls checkHighPriorityTickees.
+ */
+#define TICKER_SIGNAL SIGUSR2 /* SIGURSR1 dumps the stack */
+static pthread_t tickerThread;
+
+void
+prodHighPriorityThread()
+{
+	/* invoke the tickerThread's signal handler */
+	pthread_kill(tickerThread, TICKER_SIGNAL);
+}
+
+static void
+high_performance_tick_handler(int sig, struct siginfo *sig_info, void *context)
+{
+static int tickCheckInProgress;
+
+	if (tickCheckInProgress) return;
+
+	tickCheckInProgress = 1;
+	checkHighPriorityTickees(ioUTCMicroseconds());
+	tickCheckInProgress = 0;
+}
+
+static volatile int tickerSlumbering = 0;
+
+/* This exists only for prodHighPriorityThread to have a thread to signal. */
+static void *
+tickerSleepCycle(void *ignored)
+{
+	struct timespec naptime;
+
+	tickerSlumbering = 1;
+
+	while (1) {
+		naptime.tv_sec = 3600 * 24 * 365; /* ~ 1 year */
+		naptime.tv_nsec = 0;
+		(void)nanosleep(&naptime, 0);
+	}
+	return 0;
+}
+
+/* We require the error check because we're lazy in preventing multiple
+ * attempts at locking yield_mutex in yieldToHighPriorityTickerThread.
+ */
+#if defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP)
+# define THE_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+#elif defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
+# define THE_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER
+#else
+# define THE_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#endif
+static pthread_mutex_t yield_sync = THE_MUTEX_INITIALIZER;
+static pthread_mutex_t yield_mutex = THE_MUTEX_INITIALIZER;
+static pthread_cond_t yield_cond = PTHREAD_COND_INITIALIZER;
+
+/* Private to sqTicker.c ioSynchronousCheckForEvents */
+void
+yieldToHighPriorityTickerThread()
+{
+	int err;
+
+	if ((err = pthread_mutex_lock(&yield_mutex))) {
+		if (err != EDEADLK)
+			fprintf(stderr,"pthread_mutex_lock yield_mutex %s\n", strerror(err));
+	}
+	/* If lock fails then unblockVMThreadAfterYieldToHighPriorityTickerThread
+	 * has locked and we should not block.
+	 */
+	if ((err = pthread_mutex_lock(&yield_sync))) {
+		if (err != EDEADLK)
+			fprintf(stderr,"pthread_mutex_lock yield_sync %s\n", strerror(err));
+	}
+	else if ((err = pthread_cond_wait(&yield_cond, &yield_mutex)))
+		fprintf(stderr,"pthread_cond_wait %s\n", strerror(err));
+}
+
+/* Private to sqTicker.c checkHighPriorityTickees */
+void
+unblockVMThreadAfterYieldToHighPriorityTickerThread()
+{
+	/* If yield_sync is already locked the VM thread is very likely blocking in
+	 * yieldToHighPriorityTickerThread and so yield_cond should be signalled.
+	 */
+	if (pthread_mutex_trylock(&yield_sync) == 0) /* success */
+		pthread_mutex_unlock(&yield_sync);
+	else
+		pthread_cond_signal(&yield_cond);
+}
+
+
+#if !defined(DEFAULT_BEAT_MS)
+# define DEFAULT_BEAT_MS 2
+#endif
+static int beatMilliseconds = DEFAULT_BEAT_MS;
+
+/* Use ITIMER_REAL/SIGALRM because the VM can enter a sleep in the OS via
+ * e.g. ioRelinquishProcessorForMicroseconds in which the OS will assume the
+ * process is not running and not deliver the signals.
+ */
+#if 0
+# define THE_ITIMER ITIMER_PROF
+# define ITIMER_SIGNAL SIGPROF
+#elif 0
+# define THE_ITIMER ITIMER_VIRTUAL
+# define ITIMER_SIGNAL SIGVTALRM
+#else
+# define THE_ITIMER ITIMER_REAL
+# define ITIMER_SIGNAL SIGALRM
+#endif
+
+/* With ticker support it may be that a ticker function invoked heartbeat takes
+ * so long that another timer interrupt occurs before heartbeat has finished.
+ * The absence of SA_NODEFER in heartbeat_handler_action.sa_flags prevents
+ * reentrancy, if available.
+ *
+ * With lots of threads it may be that the kernel delivers the signal on some
+ * other thread.
+ */
+#if !defined(SA_NODEFER)
+static int handling_heartbeat = 0;
+#endif
+
+static void
+heartbeat_handler(int sig, struct siginfo *sig_info, void *context)
+{
+	if (!ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) {
+		pthread_kill(getVMOSThread(),sig);
+		return;
+	}
+
+#if !defined(SA_NODEFER)
+  {	int zeroAndPreviousHandlingHeartbeat = 0;
+    sqCompareAndSwap(handling_heartbeat,zeroAndPreviousHandlingHeartbeat,1);
+	if (zeroAndPreviousHandlingHeartbeat)
+		return;
+  }
+
+	handling_heartbeat = 1;
+#endif
+
+	heartbeat();
+
+#if 0
+	if (heartbeats % 250 == 0) {
+		printf(".");
+		fflush(stdout);
+	}
+#endif
+#if !defined(SA_NODEFER)
+	handling_heartbeat = 0;
+#endif
+}
+
+#define NEED_SIGALTSTACK 1 /* for safety; some time need to turn off and test */
+#if NEED_SIGALTSTACK
+/* If the ticker is run from the heartbeat signal handler one needs to use an
+ * alternative stack to avoid overflowing the VM's stack pages.  Keep
+ * the structure around for reference during debugging.
+ */
+#define SIGNAL_STACK_SIZE (1024 * sizeof(void *) * 16)
+static stack_t signal_stack;
+#endif /* NEED_SIGALTSTACK */
+
+void
+ioInitHeartbeat()
+{
+extern sqInt suppressHeartbeatFlag;
+	int er;
+	struct timespec halfAMo;
+	struct sigaction heartbeat_handler_action, ticker_handler_action;
+	struct itimerval pulse;
+
+	if (suppressHeartbeatFlag) return;
+
+#if NEED_SIGALTSTACK
+	signal_stack.ss_flags = 0;
+	signal_stack.ss_size = SIGNAL_STACK_SIZE;
+	if (!(signal_stack.ss_sp = malloc(signal_stack.ss_size))) {
+		perror("ioInitHeartbeat malloc");
+		exit(1);
+	}
+	if (sigaltstack(&signal_stack, 0) < 0) {
+		perror("ioInitHeartbeat sigaltstack");
+		exit(1);
+	}
+#endif /* NEED_SIGALTSTACK */
+
+	halfAMo.tv_sec  = 0;
+	halfAMo.tv_nsec = 1000 * 100;
+	if ((er= pthread_create(&tickerThread,
+							(const pthread_attr_t *)0,
+							tickerSleepCycle,
+							0))) {
+		errno = er;
+		perror("beat thread creation failed");
+		exit(errno);
+	}
+	while (!tickerSlumbering)
+		nanosleep(&halfAMo, 0);
+
+	ticker_handler_action.sa_sigaction = high_performance_tick_handler;
+	/* N.B. We _do not_ include SA_NODEFER to specifically prevent reentrancy
+	 * during the heartbeat. We /must/ include SA_RESTART to avoid issues with
+     * e.g. ODBC connections.
+	 */
+	ticker_handler_action.sa_flags = SA_RESTART | SA_ONSTACK;
+	sigemptyset(&ticker_handler_action.sa_mask);
+	if (sigaction(TICKER_SIGNAL, &ticker_handler_action, 0)) {
+		perror("ioInitHeartbeat sigaction");
+		exit(1);
+	}
+
+	heartbeat_handler_action.sa_sigaction = heartbeat_handler;
+	/* N.B. We _do not_ include SA_NODEFER to specifically prevent reentrancy
+	 * during the heartbeat.  We *must* include SA_RESTART to avoid breaking
+	 * lots of external code (e.g. the mysql odbc connect).
+	 */
+	heartbeat_handler_action.sa_flags = SA_RESTART | SA_ONSTACK;
+	sigemptyset(&heartbeat_handler_action.sa_mask);
+	if (sigaction(ITIMER_SIGNAL, &heartbeat_handler_action, 0)) {
+		perror("ioInitHeartbeat sigaction");
+		exit(1);
+	}
+
+	pulse.it_interval.tv_sec = beatMilliseconds / 1000;
+	pulse.it_interval.tv_usec = (beatMilliseconds % 1000) * 1000;
+	pulse.it_value = pulse.it_interval;
+	if (setitimer(THE_ITIMER, &pulse, &pulse)) {
+		perror("ioInitHeartbeat setitimer");
+		exit(1);
+	}
+}
+
+void
+ioSetHeartbeatMilliseconds(int ms)
+{
+	beatMilliseconds = ms;
+	ioInitHeartbeat();
+}
+
+int
+ioHeartbeatMilliseconds() { return beatMilliseconds; }
+
+
+/* Answer the average heartbeats per second since the stats were last reset.
+ */
+unsigned long
+ioHeartbeatFrequency(int resetStats)
+{
+	unsigned duration = (ioUTCMicroseconds() - get64(frequencyMeasureStart))
+						/ MicrosecondsPerSecond;
+	unsigned frequency = duration ? heartbeats / duration : 0;
+
+	if (resetStats) {
+		unsigned long long zero = 0;
+		set64(frequencyMeasureStart,zero);
+	}
+	return frequency;
+}

Modified: branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDev.c
===================================================================
--- branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDev.c	2012-01-23 19:01:42 UTC (rev 2527)
+++ branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDev.c	2012-02-02 02:03:33 UTC (rev 2528)
@@ -34,7 +34,11 @@
  *   DEALINGS IN THE SOFTWARE.
  */
 
+/*
+ *   last update: 31 Jan 2012 13:38:32 CET; Michael J. Zeder
+ */
 
+
 #include "config.h"
 #include "sq.h"
 #include "sqUnixMain.h"
@@ -321,7 +325,7 @@
 }
 
 
-static void display_winOpen(void)
+static void display_winOpen(int argc, char *dropFiles[])
 {
   openDisplay();
 }
@@ -404,7 +408,7 @@
 
 static sqInt display_dndOutStart(char *types, int ntypes)	{ return 0; }
 static void  display_dndOutSend(char *bytes, int nbytes)	{ return  ; }
-static void  display_dndLaunchFile(char *fileName)	{ return ; }
+/* UNUSED static void  display_dndLaunchFile(char *fileName)	{ return ; }  */
 static sqInt display_dndOutAcceptedType(char * buf, int nbuf)	{ return 0; }
 static sqInt display_dndReceived(char *fileName)	{ return 0; }
 
@@ -466,6 +470,19 @@
 #endif
 
 
+
+// new stubs for the CogVM
+sqInt display_ioSetCursorPositionXY(sqInt x, sqInt y) { return 0; }
+int display_ioPositionOfScreenWorkArea (int windowIndex) { return -1; }
+int display_ioSizeOfScreenWorkArea (int windowIndex) { return -1; }
+void *display_ioGetWindowHandle() { return 0; }
+int display_ioPositionOfNativeDisplay(void *windowHandle) { return -1; }
+int display_ioSizeOfNativeDisplay(void *windowHandle) { return -1; }
+int display_ioPositionOfNativeWindow(void *windowHandle) { return -1; }
+int display_ioSizeOfNativeWindow(void *windowHandle) { return -1; }
+
+
+
 //----------------------------------------------------------------
 
 

Modified: branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDevMousePS2.c
===================================================================
--- branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDevMousePS2.c	2012-01-23 19:01:42 UTC (rev 2527)
+++ branches/Cog/platforms/unix/vm-display-fbdev/sqUnixFBDevMousePS2.c	2012-02-02 02:03:33 UTC (rev 2528)
@@ -2,7 +2,7 @@
  * 
  * Author: Ian.Piumarta at INRIA.Fr
  * 
- * Last edited: 2003-10-31 11:42:56 by piumarta on emilia.inria.fr
+ * Last edited: 31 Jan 2012 12:57:17 CET Michael J. Zeder
  */
 
 /* The framebuffer display driver was donated to the Squeak community by:
@@ -110,7 +110,11 @@
   for (i= 0;  i < len;  ++i)
     {
     resend:
-      write(self->fd, command + i, 1);
+      if (1 != write(self->fd, command + i, 1))
+	{
+	  fprintf(stderr, "%s: could not write command to ps2\n", self->msName);
+	  return 0;
+	}
       DPRINTF(">%02x\n", command[i]);
       if (1 != ms_read(self, buf, 1, 1, PS2_SEND_DELAY))
 	{
@@ -120,7 +124,7 @@
       switch (buf[0])
 	{
 	case PS2_OK:
-	case PS2_SELFTEST_OK:	/* /dev/input/mice emulation is broken */
+	case PS2_SELFTEST_OK:	 /* /dev/input/mice emulation is broken */
 	  break;
 	case PS2_ERROR:
 	  fprintf(stderr, "%s: error response in send\n", self->msName);
@@ -138,10 +142,14 @@
 
 
 static void ms_ps2_disable(_self)
-{
+{ 
   unsigned char command[]= { PS2_DISABLE };
   DPRINTF("%s: disable\n", self->msName);
-  write(self->fd, command, 1);
+  if (1 != write(self->fd, command, 1))
+    {
+	fprintf(stderr, "%s: could not write command to ps2\n", self->msName);
+	return;
+    }
   DPRINTF(">%02x\n", command[0]);
   while (1 == ms_read(self, command, 1, 1, PS2_DISABLE_DELAY))
     if (PS2_OK == command[0])

Modified: branches/Cog/src/plugins/SqueakFFIPrims/SqueakFFIPrims.c
===================================================================
--- branches/Cog/src/plugins/SqueakFFIPrims/SqueakFFIPrims.c	2012-01-23 19:01:42 UTC (rev 2527)
+++ branches/Cog/src/plugins/SqueakFFIPrims/SqueakFFIPrims.c	2012-02-02 02:03:33 UTC (rev 2528)
@@ -1,9 +1,9 @@
 /* Automatically generated by
-	VMPluginCodeGenerator VMMaker.oscog-eem.125 uuid: 539c10db-ab19-4fc7-ada1-4ffae6463ed1
+	VMPluginCodeGenerator VMMaker.oscog-eem.142 uuid: 5b4312fa-5b57-4639-b12d-a45e13e5bbdc
    from
-	ThreadedIA32FFIPlugin VMMaker.oscog-eem.125 uuid: 539c10db-ab19-4fc7-ada1-4ffae6463ed1
+	ThreadedIA32FFIPlugin VMMaker.oscog-eem.142 uuid: 5b4312fa-5b57-4639-b12d-a45e13e5bbdc
  */
-static char __buildInfo[] = "ThreadedIA32FFIPlugin VMMaker.oscog-eem.125 uuid: 539c10db-ab19-4fc7-ada1-4ffae6463ed1 " __DATE__ ;
+static char __buildInfo[] = "ThreadedIA32FFIPlugin VMMaker.oscog-eem.142 uuid: 5b4312fa-5b57-4639-b12d-a45e13e5bbdc " __DATE__ ;
 
 
 
@@ -132,6 +132,7 @@
 /*** Constants ***/
 #define BaseHeaderSize 4
 #define BytesPerOop 4
+#define BytesPerWord 4
 #define DefaultMaxStackSize 16384
 #define ExternalFunctionArgTypesIndex 2
 #define ExternalFunctionFlagsIndex 1
@@ -275,9 +276,9 @@
 struct VirtualMachine* interpreterProxy;
 static const char *moduleName =
 #ifdef SQUEAK_BUILTIN_PLUGIN
-	"SqueakFFIPrims VMMaker.oscog-eem.125 (i)"
+	"SqueakFFIPrims VMMaker.oscog-eem.142 (i)"
 #else
-	"SqueakFFIPrims VMMaker.oscog-eem.125 (e)"
+	"SqueakFFIPrims VMMaker.oscog-eem.142 (e)"
 #endif
 ;
 
@@ -1371,29 +1372,6 @@
 	if (interpreterProxy->failed()) {
 		return ffiFail(FFIErrorBadArgs);
 	}
-
-	/* must be array of arg types */
-
-	argTypeArray = interpreterProxy->fetchPointerofObject(ExternalFunctionArgTypesIndex, externalFunction);
-	if (!((interpreterProxy->isArray(argTypeArray))
-		 && ((interpreterProxy->slotSizeOf(argTypeArray)) == (nArgs + 1)))) {
-		return ffiFail(FFIErrorBadArgs);
-	}
-	
-#  if COGMTVM
-	if (!(((flags & FFICallTypesMask) == FFICallTypeCDecl)
-		 || ((flags & FFICallTypesMask) == FFICallTypeApi))) {
-		return ffiFail(FFIErrorCallType);
-	}
-
-#  else /* COGMTVM */
-	if (!((flags == FFICallTypeCDecl)
-		 || (flags == FFICallTypeApi))) {
-		return ffiFail(FFIErrorCallType);
-	}
-
-#  endif /* COGMTVM */
-
 	/* begin ffiLoadCalloutAddress: */
 
 	/* Lookup the address */
@@ -1440,6 +1418,29 @@
 	if (interpreterProxy->failed()) {
 		return 0;
 	}
+
+	/* must be array of arg types */
+
+	argTypeArray = interpreterProxy->fetchPointerofObject(ExternalFunctionArgTypesIndex, externalFunction);
+	if (!((interpreterProxy->isArray(argTypeArray))
+		 && ((interpreterProxy->slotSizeOf(argTypeArray)) == (nArgs + 1)))) {
+		return ffiFail(FFIErrorBadArgs);
+	}
+	
+#  if COGMTVM
+	if (!(((flags & FFICallTypesMask) == FFICallTypeCDecl)
+		 || ((flags & FFICallTypesMask) == FFICallTypeApi))) {
+		return ffiFail(FFIErrorCallType);
+	}
+
+#  else /* COGMTVM */
+	if (!((flags == FFICallTypeCDecl)
+		 || (flags == FFICallTypeApi))) {
+		return ffiFail(FFIErrorCallType);
+	}
+
+#  endif /* COGMTVM */
+
 	requiredStackSize = (externalFunctionInstSize > ExternalFunctionStackSizeIndex
 		? interpreterProxy->fetchIntegerofObject(ExternalFunctionStackSizeIndex, externalFunction)
 		: -1);
@@ -2914,32 +2915,6 @@
 		ffiFail(FFIErrorBadArgs);
 		goto l3;
 	}
-
-	/* must be array of arg types */
-
-	argTypeArray = interpreterProxy->fetchPointerofObject(ExternalFunctionArgTypesIndex, externalFunction);
-	if (!((interpreterProxy->isArray(argTypeArray))
-		 && ((interpreterProxy->slotSizeOf(argTypeArray)) == (nArgs1 + 1)))) {
-		ffiFail(FFIErrorBadArgs);
-		goto l3;
-	}
-	
-#  if COGMTVM
-	if (!(((flags & FFICallTypesMask) == FFICallTypeCDecl)
-		 || ((flags & FFICallTypesMask) == FFICallTypeApi))) {
-		ffiFail(FFIErrorCallType);
-		goto l3;
-	}
-
-#  else /* COGMTVM */
-	if (!((flags == FFICallTypeCDecl)
-		 || (flags == FFICallTypeApi))) {
-		ffiFail(FFIErrorCallType);
-		goto l3;
-	}
-
-#  endif /* COGMTVM */
-
 	/* begin ffiLoadCalloutAddress: */
 
 	/* Lookup the address */
@@ -2986,6 +2961,32 @@
 	if (interpreterProxy->failed()) {
 		goto l3;
 	}
+
+	/* must be array of arg types */
+
+	argTypeArray = interpreterProxy->fetchPointerofObject(ExternalFunctionArgTypesIndex, externalFunction);
+	if (!((interpreterProxy->isArray(argTypeArray))
+		 && ((interpreterProxy->slotSizeOf(argTypeArray)) == (nArgs1 + 1)))) {
+		ffiFail(FFIErrorBadArgs);
+		goto l3;
+	}
+	
+#  if COGMTVM
+	if (!(((flags & FFICallTypesMask) == FFICallTypeCDecl)
+		 || ((flags & FFICallTypesMask) == FFICallTypeApi))) {
+		ffiFail(FFIErrorCallType);
+		goto l3;
+	}
+
+#  else /* COGMTVM */
+	if (!((flags == FFICallTypeCDecl)
+		 || (flags == FFICallTypeApi))) {
+		ffiFail(FFIErrorCallType);
+		goto l3;
+	}
+
+#  endif /* COGMTVM */
+
 	requiredStackSize = (externalFunctionInstSize > ExternalFunctionStackSizeIndex
 		? interpreterProxy->fetchIntegerofObject(ExternalFunctionStackSizeIndex, externalFunction)
 		: -1);
@@ -3311,32 +3312,6 @@
 		ffiFail(FFIErrorBadArgs);
 		goto l3;
 	}
-
-	/* must be array of arg types */
-
-	argTypeArray = interpreterProxy->fetchPointerofObject(ExternalFunctionArgTypesIndex, externalFunction);
-	if (!((interpreterProxy->isArray(argTypeArray))
-		 && ((interpreterProxy->slotSizeOf(argTypeArray)) == (nArgs + 1)))) {
-		ffiFail(FFIErrorBadArgs);
-		goto l3;
-	}
-	
-#  if COGMTVM
-	if (!(((flags & FFICallTypesMask) == FFICallTypeCDecl)
-		 || ((flags & FFICallTypesMask) == FFICallTypeApi))) {
-		ffiFail(FFIErrorCallType);
-		goto l3;
-	}
-
-#  else /* COGMTVM */
-	if (!((flags == FFICallTypeCDecl)
-		 || (flags == FFICallTypeApi))) {
-		ffiFail(FFIErrorCallType);
-		goto l3;
-	}
-
-#  endif /* COGMTVM */
-
 	/* begin ffiLoadCalloutAddress: */
 
 	/* Lookup the address */
@@ -3383,6 +3358,32 @@
 	if (interpreterProxy->failed()) {
 		goto l3;
 	}
+
+	/* must be array of arg types */
+
+	argTypeArray = interpreterProxy->fetchPointerofObject(ExternalFunctionArgTypesIndex, externalFunction);
+	if (!((interpreterProxy->isArray(argTypeArray))
+		 && ((interpreterProxy->slotSizeOf(argTypeArray)) == (nArgs + 1)))) {
+		ffiFail(FFIErrorBadArgs);
+		goto l3;
+	}
+	
+#  if COGMTVM
+	if (!(((flags & FFICallTypesMask) == FFICallTypeCDecl)
+		 || ((flags & FFICallTypesMask) == FFICallTypeApi))) {
+		ffiFail(FFIErrorCallType);
+		goto l3;
+	}
+
+#  else /* COGMTVM */
+	if (!((flags == FFICallTypeCDecl)
+		 || (flags == FFICallTypeApi))) {
+		ffiFail(FFIErrorCallType);
+		goto l3;
+	}
+
+#  endif /* COGMTVM */
+
 	requiredStackSize = (externalFunctionInstSize > ExternalFunctionStackSizeIndex
 		? interpreterProxy->fetchIntegerofObject(ExternalFunctionStackSizeIndex, externalFunction)
 		: -1);

Modified: branches/Cog/unixbuild/bld/mvm
===================================================================
--- branches/Cog/unixbuild/bld/mvm	2012-01-23 19:01:42 UTC (rev 2527)
+++ branches/Cog/unixbuild/bld/mvm	2012-02-02 02:03:33 UTC (rev 2528)
@@ -8,4 +8,4 @@
 test -f config.h || ../../platforms/unix/config/configure --without-vm-display-fbdev --without-npsqueak CFLAGS="-g $OPT -msse2 -D_GNU_SOURCE -DNDEBUG -DITIMER_HEARTBEAT=1 -DNO_VM_PROFILE=1 -DCOGMTVM=0 -DDEBUGVM=0" LIBS=-lpthread
 ../../scripts/nukeversion
 rm -rf ../../coglinux
-make install prefix=`readlink -f \`pwd\`/../../coglinux`
+make install prefix=`(cd ../../;pwd)`/coglinux

Modified: branches/Cog/unixbuild/mtbld/mvm
===================================================================
--- branches/Cog/unixbuild/mtbld/mvm	2012-01-23 19:01:42 UTC (rev 2527)
+++ branches/Cog/unixbuild/mtbld/mvm	2012-02-02 02:03:33 UTC (rev 2528)
@@ -8,4 +8,4 @@
 test -f config.h || ../../platforms/unix/config/configure INTERP=cointerpmt --without-vm-display-fbdev --without-npsqueak CFLAGS="-g $OPT -msse2 -DNDEBUG -DCOGMTVM=1 -DDEBUGVM=0 -D_GNU_SOURCE -DITIMER_HEARTBEAT=1 -DNO_VM_PROFILE=1" LIBS=-lpthread
 ../../scripts/nukeversion
 rm -rf ../../cogmtlinux
-make install prefix=`readlink -f \`pwd\`/../../cogmtlinux`
+make install prefix=`(cd ../../;pwd)`/cogmtlinux



More information about the Vm-dev mailing list