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@qwaq.com +* EMAIL: eliot.miranda@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@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@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@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
vm-dev@lists.squeakfoundation.org