[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