diff --git a/configure.ac b/configure.ac index 6129b0f..802a5a8 100644 --- a/configure.ac +++ b/configure.ac @@ -292,6 +292,7 @@ AC_CHECK_LIB(thr,pthread_self,,[ AC_CHECK_LIB(rt,clock_gettime,,) AC_CHECK_FUNCS([clock_gettime]) +AC_CHECK_FUNCS([pthread_condattr_setclock]) AC_CHECK_LIB(m,fmod,,AC_MSG_ERROR(libm is missing)) diff --git a/src/classlib/openjdk/jvm.c b/src/classlib/openjdk/jvm.c index cd31b4d..27c5029 100644 --- a/src/classlib/openjdk/jvm.c +++ b/src/classlib/openjdk/jvm.c @@ -57,12 +57,6 @@ #define JVM_INTERFACE_VERSION 4 -/* We use the monotonic clock if it is available. As the clock_id may be - present but not actually supported, we check it on startup */ -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) -static int have_monotonic_clock; -#endif - static Class *cloneable_class, *constant_pool_class; static Class *exception_class, *runtime_excp_class; @@ -72,11 +66,6 @@ static int constant_pool_oop_offset; int initialiseJVMInterface() { Class *pae_class; -#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; - have_monotonic_clock = (clock_gettime(CLOCK_MONOTONIC, &ts) != -1); -#endif - cloneable_class = findSystemClass0(SYMBOL(java_lang_Cloneable)); constant_pool_class = findSystemClass0(SYMBOL(sun_reflect_ConstantPool)); exception_class = findSystemClass0(SYMBOL(java_lang_Exception)); @@ -176,7 +165,7 @@ jlong JVM_NanoTime(JNIEnv *env, jclass ignored) { TRACE("JVM_NanoTime(env=%p, ignored=%p)", env, ignored); #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - if(have_monotonic_clock) { + if(haveMonotonicClock()) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); diff --git a/src/init.c b/src/init.c index 38ad54b..fdc46c8 100644 --- a/src/init.c +++ b/src/init.c @@ -99,6 +99,7 @@ int initVM(InitArgs *args) { status = initialiseHooks(args) && initialiseProperties(args) && + initialiseTime() && initialiseAlloc(args) && initialiseThreadStage1(args) && initialiseUtf8() && diff --git a/src/jam.h b/src/jam.h index ce73df6..8e8af0d 100644 --- a/src/jam.h +++ b/src/jam.h @@ -24,6 +24,7 @@ #include #include #include +#include /* Configure options */ #include "config.h" @@ -1300,10 +1301,13 @@ extern int initialiseSymbol(); /* time */ +extern int initialiseTime(); +extern int haveMonotonicClock(); extern void getTimeoutAbsolute(struct timespec *ts, long long millis, long long nanos); extern void getTimeoutRelative(struct timespec *ts, long long millis, long long nanos); +extern int initReltimeCondVar(pthread_cond_t *cv); /* sig */ diff --git a/src/thread.c b/src/thread.c index 8ca1fbc..56ae35e 100644 --- a/src/thread.c +++ b/src/thread.c @@ -248,15 +248,19 @@ void threadPark(Thread *self, int absolute, long long time) { disableSuspend(self); if(time) { + pthread_cond_t *cv; struct timespec ts; - if(absolute) + if(absolute) { getTimeoutAbsolute(&ts, time, 0); - else + cv = &self->park_cv_abs; + } else { getTimeoutRelative(&ts, 0, time); + cv = &self->park_cv; + } classlibSetThreadState(self, TIMED_PARKED); - pthread_cond_timedwait(&self->park_cv, &self->park_lock, &ts); + pthread_cond_timedwait(cv, &self->park_lock, &ts); /* On Linux/i386 systems using LinuxThreads, pthread_cond_timedwait is implemented using sigjmp/longjmp. This resets the fpu @@ -265,6 +269,7 @@ void threadPark(Thread *self, int absolute, long long time) { FPU_HACK; } else { + /* Arbitrary choice of condvar when not timed */ classlibSetThreadState(self, PARKED); pthread_cond_wait(&self->park_cv, &self->park_lock); } @@ -294,11 +299,16 @@ void threadUnpark(Thread *thread) { /* If another thread has given a permit while we were waiting for the lock do nothing. Else increase the state by one (BLOCKED -> RUNNING, RUNNING -> PERMIT) - and if the thread was blocked signal it */ + and if the thread was blocked signal it. The thread + waits on park_cv or park_cv_abs depending on the type + of timeout; signalling an un-waited condvar is harmless + and cheap, so simply signal both */ if(thread->park_state != PARK_PERMIT && - thread->park_state++ == PARK_BLOCKED) + thread->park_state++ == PARK_BLOCKED) { pthread_cond_signal(&thread->park_cv); + pthread_cond_signal(&thread->park_cv_abs); + } pthread_mutex_unlock(&thread->park_lock); } @@ -483,12 +493,13 @@ void initThread(Thread *thread, char is_daemon, void *stack_base) { /* Initialise wait condvar (the condvar is per-thread, not per-monitor) */ - pthread_cond_init(&thread->wait_cv, NULL); + initReltimeCondVar(&thread->wait_cv); - /* Initialise per-thread lock/condvar used for parking + /* Initialise per-thread lock/condvars used for parking and set initial park state */ thread->park_state = PARK_RUNNING; - pthread_cond_init(&thread->park_cv, NULL); + initReltimeCondVar(&thread->park_cv); + pthread_cond_init(&thread->park_cv_abs, NULL); pthread_mutex_init(&thread->park_lock, NULL); /* Record the thread's stack base */ @@ -1307,10 +1318,11 @@ int initialiseThreadStage1(InitArgs *args) { initialiseJavaStack(&main_ee); setThreadSelf(&main_thread); - pthread_cond_init(&main_thread.wait_cv, NULL); + initReltimeCondVar(&main_thread.wait_cv); main_thread.park_state = PARK_RUNNING; - pthread_cond_init(&main_thread.park_cv, NULL); + initReltimeCondVar(&main_thread.park_cv); + pthread_cond_init(&main_thread.park_cv_abs, NULL); pthread_mutex_init(&main_thread.park_lock, NULL); return TRUE; diff --git a/src/thread.h b/src/thread.h index 4c1d77b..5544802 100644 --- a/src/thread.h +++ b/src/thread.h @@ -103,6 +103,7 @@ struct thread { Thread *wait_next; pthread_cond_t wait_cv; pthread_cond_t park_cv; + pthread_cond_t park_cv_abs; pthread_mutex_t park_lock; long long blocked_count; long long waited_count; @@ -189,7 +190,7 @@ typedef pthread_mutex_t VMLock; #define initVMLock(lock) pthread_mutex_init(&lock, NULL) #define initVMWaitLock(wait_lock) { \ pthread_mutex_init(&wait_lock.lock, NULL); \ - pthread_cond_init(&wait_lock.cv, NULL); \ + initReltimeCondVar(&wait_lock.cv); \ } #define lockVMLock(lock, self) { \ @@ -213,15 +214,8 @@ typedef pthread_mutex_t VMLock; } #define timedWaitVMWaitLock(wait_lock, self, ms) { \ - struct timeval tv; \ struct timespec ts; \ - gettimeofday(&tv, 0); \ - ts.tv_sec = tv.tv_sec + ms/1000; \ - ts.tv_nsec = (tv.tv_usec + ((ms%1000)*1000))*1000; \ - if(ts.tv_nsec > 999999999L) { \ - ts.tv_sec++; \ - ts.tv_nsec -= 1000000000L; \ - } \ + getTimeoutRelative(&ts, ms, 0); \ classlibSetThreadState(self, TIMED_WAITING); \ pthread_cond_timedwait(&wait_lock.cv, &wait_lock.lock, &ts); \ classlibSetThreadState(self, RUNNING); \ diff --git a/src/time.c b/src/time.c index 8537e7a..a9b7f43 100644 --- a/src/time.c +++ b/src/time.c @@ -21,6 +21,63 @@ #include #include #include +#include + +#include "jam.h" + +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) && \ + defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) +#define MONOTONIC_CONDWAIT +#endif + +/* We use the monotonic clock if it is available. As the clock_id may be + present but not actually supported, we check it on startup */ +static int have_monotonic_clock = FALSE; + +/* For relative timeouts, additionally check that condvars can be bound + to the monotonic clock */ +static int have_monotonic_condwait = FALSE; + +#ifdef MONOTONIC_CONDWAIT +static pthread_condattr_t monotonic_condattr; +#endif + +int initialiseTime() { +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + struct timespec ts; + have_monotonic_clock = (clock_gettime(CLOCK_MONOTONIC, &ts) != -1); + +#ifdef MONOTONIC_CONDWAIT + if(have_monotonic_clock && + pthread_condattr_init(&monotonic_condattr) == 0) { + if(pthread_condattr_setclock(&monotonic_condattr, + CLOCK_MONOTONIC) == 0) + have_monotonic_condwait = TRUE; + else + pthread_condattr_destroy(&monotonic_condattr); + } +#endif +#endif + + if(!have_monotonic_condwait) + jam_fprintf(stderr, "Monotonic clock not available. Changes to " + "the current date/time may affect scheduling.\n"); + + return TRUE; +} + +int haveMonotonicClock() { + return have_monotonic_clock; +} + +/* Initialise a condition variable to be used for relative timed waits */ +int initReltimeCondVar(pthread_cond_t *cv) { +#ifdef MONOTONIC_CONDWAIT + if(have_monotonic_condwait) + return pthread_cond_init(cv, &monotonic_condattr); +#endif + return pthread_cond_init(cv, NULL); +} void getTimeoutAbsolute(struct timespec *ts, long long millis, long long nanos) { @@ -46,18 +103,36 @@ void getTimeoutAbsolute(struct timespec *ts, long long millis, void getTimeoutRelative(struct timespec *ts, long long millis, long long nanos) { - struct timeval tv; long long seconds; - /* Get the current time */ - gettimeofday(&tv, NULL); +#ifdef MONOTONIC_CONDWAIT + if(have_monotonic_condwait) { + struct timespec now; - /* Calculate seconds (long long prevents overflow) */ - seconds = tv.tv_sec + millis / 1000 + nanos / 1000000000; + /* Get the current time */ + clock_gettime(CLOCK_MONOTONIC, &now); - /* Calculate nanoseconds */ - nanos %= 1000000000; - nanos += (tv.tv_usec + ((millis % 1000) * 1000)) * 1000; + /* Calculate seconds (long long prevents overflow) */ + seconds = now.tv_sec + millis / 1000 + nanos / 1000000000; + + /* Calculate nanoseconds */ + nanos %= 1000000000; + nanos += now.tv_nsec + (millis % 1000) * 1000000; + } else +#endif + { + struct timeval tv; + + /* Get the current time */ + gettimeofday(&tv, NULL); + + /* Calculate seconds (long long prevents overflow) */ + seconds = tv.tv_sec + millis / 1000 + nanos / 1000000000; + + /* Calculate nanoseconds */ + nanos %= 1000000000; + nanos += (tv.tv_usec + ((millis % 1000) * 1000)) * 1000; + } /* Adjust values so that nanos is less than 1 second. This also prevents overflowing the timespec, as the @@ -66,7 +141,8 @@ void getTimeoutRelative(struct timespec *ts, long long millis, nanos %= 1000000000; /* If seconds is too big to fit into the timespec use the - maximum value (year 2038) */ + maximum value (for 32-bit time_t and realtime clock, + year 2038) */ ts->tv_sec = seconds > LONG_MAX ? LONG_MAX : seconds; ts->tv_nsec = nanos; }