From b834f1fa0659d66d4c2d033cdb3d0f2a1f1ce390 Mon Sep 17 00:00:00 2001 From: Bryce McKinlay Date: Tue, 28 Mar 2000 02:22:24 +0000 Subject: Makefile.in: New #defines and friends for Thread.h. * Makefile.in: New #defines and friends for Thread.h. * posix-threads.cc: (struct starter): Remove `object'. (_Jv_CondWait): Use interruptable condition variables and new recursive mutexes. New return codes on interrupt or non-ownership of mutex. (_Jv_CondNotify): Ditto. (_Jv_CondNotifyAll): Ditto. (_Jv_ThreadInterrupt): Set thread interrupt flag directly. Interrupt the target thread by signaling its wait condition. (_Jv_ThreadInitData): Set `thread_obj' in the thread data struct, not the starter struct. Initialize wait_mutex and wait_cond. (_Jv_MutexLock): New recursive mutex implementation. Moved from posix-threads.h. (_Jv_MutexUnlock): Ditto. (really_start): Set info->data->thread from pthread_self() to work around a race condition. Destroy wait_mutex and wait_cond when run() returns. * java/lang/Thread.java: (isInterrupted_): Renamed to overloaded `isInterrupted(boolean)'. Clear interrupted flag if clear_flag is set. startable_flag: New private field. (Thread): Initialize `startable_flag'. (toString): Check for null thread group. * java/lang/natThread.cc: (struct natThread): New fields `join_mutex', `join_cond'. Removed fields `joiner', `next'. (class locker): Removed. (initialize_native): Initialize `join_cond' and `join_mutex'. (interrupt): Now just calls _Jv_ThreadInterrupt(). (join): Simplified. Just wait on the target thread's join condition. (finish_): Remove join list code. Unset thread group. Signal potential joiners by notifying the dying threads join_cond. (start): Check for illegal restarts. * java/lang/natObject.cc: Check for return value of _Jv_CondWait and act appropriatly. * include/posix-threads.h: Remove all HAVE_RECURSIVE_MUTEX related #defines and #ifdefs. (struct _Jv_Thread_t): New fields `thread_obj', `wait_cond', `wait_mutex', `next'. (struct _Jv_ConditionVariable_t): Define as a struct instead of directly mapping to pthread_cond_t. (struct _Jv_Mutex_t): New recursive implementation. (_Jv_PthreadCheckMonitor): Reimplemented. Simple `owner' check. _Jv_HaveCondDestroy: Never define this for posix-threads. (_Jv_CondNotify): Remove inline implementation(s), prototype instead. (_Jv_CondNotifyAll): Ditto. (_Jv_MutexLock): Ditto. (_Jv_MutexUnlock): Ditto. (_Jv_MutexInit): Changed to reflect new mutex implementation. (_Jv_MutexDestroy): Ditto. (_Jv_CondDestroy): Removed. (_Jv_PthreadGetMutex): Removed. * include/win32-threads.h: (_Jv_CondNotify): Guess _JV_NOT_OWNER on an error. Add a FIXME about this. (_Jv_CondNotifyAll): Ditto. * win32-threads.cc: (_Jv_CondWait): Return 0 on a timeout. Guess _JV_NOT_OWNER on other errors. Add FIXME. From-SVN: r32773 --- libjava/ChangeLog | 59 +++++++ libjava/include/posix-threads.h | 217 ++++++----------------- libjava/include/win32-threads.h | 6 +- libjava/java/lang/Thread.java | 24 ++- libjava/java/lang/natObject.cc | 14 +- libjava/java/lang/natThread.cc | 164 ++++-------------- libjava/posix-threads.cc | 374 ++++++++++++++++++++++------------------ libjava/win32-threads.cc | 6 +- 8 files changed, 382 insertions(+), 482 deletions(-) diff --git a/libjava/ChangeLog b/libjava/ChangeLog index dd2a68d..6aee33f 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,62 @@ +2000-03-27 Bryce McKinlay + + * Makefile.in: New #defines and friends for Thread.h. + * posix-threads.cc: (struct starter): Remove `object'. + (_Jv_CondWait): Use interruptable condition variables and new + recursive mutexes. New return codes on interrupt or non-ownership + of mutex. + (_Jv_CondNotify): Ditto. + (_Jv_CondNotifyAll): Ditto. + (_Jv_ThreadInterrupt): Set thread interrupt flag directly. Interrupt + the target thread by signaling its wait condition. + (_Jv_ThreadInitData): Set `thread_obj' in the thread data struct, + not the starter struct. Initialize wait_mutex and wait_cond. + (_Jv_MutexLock): New recursive mutex implementation. Moved from + posix-threads.h. + (_Jv_MutexUnlock): Ditto. + (really_start): Set info->data->thread from pthread_self() to work + around a race condition. Destroy wait_mutex and wait_cond when run() + returns. + * java/lang/Thread.java: (isInterrupted_): Renamed to overloaded + `isInterrupted(boolean)'. Clear interrupted flag if clear_flag is + set. + startable_flag: New private field. + (Thread): Initialize `startable_flag'. + (toString): Check for null thread group. + * java/lang/natThread.cc: (struct natThread): New fields + `join_mutex', `join_cond'. Removed fields `joiner', `next'. + (class locker): Removed. + (initialize_native): Initialize `join_cond' and `join_mutex'. + (interrupt): Now just calls _Jv_ThreadInterrupt(). + (join): Simplified. Just wait on the target thread's join condition. + (finish_): Remove join list code. Unset thread group. Signal + potential joiners by notifying the dying threads join_cond. + (start): Check for illegal restarts. + * java/lang/natObject.cc: Check for return value of _Jv_CondWait and + act appropriatly. + * include/posix-threads.h: Remove all HAVE_RECURSIVE_MUTEX related + #defines and #ifdefs. + (struct _Jv_Thread_t): New fields `thread_obj', `wait_cond', + `wait_mutex', `next'. + (struct _Jv_ConditionVariable_t): Define as a struct instead of + directly mapping to pthread_cond_t. + (struct _Jv_Mutex_t): New recursive implementation. + (_Jv_PthreadCheckMonitor): Reimplemented. Simple `owner' check. + _Jv_HaveCondDestroy: Never define this for posix-threads. + (_Jv_CondNotify): Remove inline implementation(s), prototype instead. + (_Jv_CondNotifyAll): Ditto. + (_Jv_MutexLock): Ditto. + (_Jv_MutexUnlock): Ditto. + (_Jv_MutexInit): Changed to reflect new mutex implementation. + (_Jv_MutexDestroy): Ditto. + (_Jv_CondDestroy): Removed. + (_Jv_PthreadGetMutex): Removed. + * include/win32-threads.h: (_Jv_CondNotify): Guess _JV_NOT_OWNER on an + error. Add a FIXME about this. + (_Jv_CondNotifyAll): Ditto. + * win32-threads.cc: (_Jv_CondWait): Return 0 on a timeout. Guess + _JV_NOT_OWNER on other errors. Add FIXME. + 2000-03-26 Tom Tromey * jni.cc (_Jv_JNI_PopSystemFrame): If environment has exception diff --git a/libjava/include/posix-threads.h b/libjava/include/posix-threads.h index ac74ebe..22f6717 100644 --- a/libjava/include/posix-threads.h +++ b/libjava/include/posix-threads.h @@ -20,81 +20,56 @@ details. */ #include #include -#if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE) || defined (HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) -# define HAVE_RECURSIVE_MUTEX 1 -#endif - - // // Typedefs. // -typedef pthread_cond_t _Jv_ConditionVariable_t; - -#if defined (PTHREAD_MUTEX_HAVE_M_COUNT) || defined (PTHREAD_MUTEX_HAVE___M_COUNT) +typedef struct _Jv_Thread_t +{ + // Flag values are defined in implementation. + int flags; -// On Linux we use implementation details of mutexes in order to get -// faster results. -typedef pthread_mutex_t _Jv_Mutex_t; + // Actual thread id. + pthread_t thread; + + // Java Thread object. + java::lang::Thread *thread_obj; + + // Condition variable and corresponding mutex, used to implement the + // interruptable wait/notify mechanism. + pthread_cond_t wait_cond; + pthread_mutex_t wait_mutex; + + // Next thread for Condition Variable wait-list chain. + _Jv_Thread_t *next; + +} _Jv_Thread_t; -#else /* LINUX_THREADS */ +typedef void _Jv_ThreadStartFunc (java::lang::Thread *); -#define PTHREAD_MUTEX_IS_STRUCT +// Condition Variables used to implement wait/notify/sleep/interrupt. typedef struct { - // Mutex used when locking this structure transiently. - pthread_mutex_t mutex; -#ifndef HAVE_RECURSIVE_MUTEX - // Some systems do not have recursive mutexes, so we must simulate - // them. Solaris is one such system. - - // Mutex the thread holds the entire time this mutex is held. This - // is used to make condition variables work properly. - pthread_mutex_t mutex2; - // Condition variable used when waiting for this lock. - pthread_cond_t cond; - // Thread holding this mutex. If COUNT is 0, no thread is holding. - pthread_t thread; -#endif /* HAVE_RECURSIVE_MUTEX */ - - // Number of times mutex is held. If 0, the lock is not held. We - // do this even if we have a native recursive mutex so that we can - // keep track of whether the lock is held; this lets us do error - // checking. FIXME it would be nice to optimize this; on some - // systems we could do so by relying on implementation details of - // recursive mutexes. - int count; -} _Jv_Mutex_t; + // Linked list of Threads that are waiting to be notified. + _Jv_Thread_t *first; -#endif +} _Jv_ConditionVariable_t; typedef struct { - // Flag values are defined in implementation. - int flags; - - // Actual thread id. - pthread_t thread; -} _Jv_Thread_t; -typedef void _Jv_ThreadStartFunc (java::lang::Thread *); + // For compatibility, simplicity, and correctness, we do not use the native + // pthreads recursive mutex implementation, but simulate them instead. + // Mutex the thread holds the entire time this mutex is held. + pthread_mutex_t mutex; -// This convenience function is used to return the POSIX mutex -// corresponding to our mutex. -inline pthread_mutex_t * -_Jv_PthreadGetMutex (_Jv_Mutex_t *mu) -{ -#if ! defined (PTHREAD_MUTEX_IS_STRUCT) - return mu; -#elif defined (HAVE_RECURSIVE_MUTEX) - return &mu->mutex; -#else - return &mu->mutex2; -#endif -} + // Thread holding this mutex. + pthread_t owner; -#include + // Number of times mutex is held (lock depth). If 0, the lock is not held. + int count; +} _Jv_Mutex_t; // This is a convenience function used only by the pthreads thread // implementation. This is slow, but that's too bad -- we need to do @@ -104,95 +79,44 @@ _Jv_PthreadGetMutex (_Jv_Mutex_t *mu) inline int _Jv_PthreadCheckMonitor (_Jv_Mutex_t *mu) { - pthread_mutex_t *pmu; -#ifdef HAVE_RECURSIVE_MUTEX - pmu = _Jv_PthreadGetMutex (mu); - // See if the mutex is locked by this thread. - if (pthread_mutex_trylock (pmu)) - return 1; - -#if defined (PTHREAD_MUTEX_HAVE_M_COUNT) - // On Linux we exploit knowledge of the implementation. - int r = pmu->m_count == 1; -#elif defined (PTHREAD_MUTEX_HAVE___M_COUNT) - // In glibc 2.1, the first time the mutex is grabbed __m_count is - // set to 0 and __m_owner is set to pthread_self(). - int r = ! pmu->__m_count; -#else - int r = mu->count == 0; -#endif - -#else /* HAVE_RECURSIVE_MUTEX */ - // In this case we must lock our structure and then see if this - // thread owns the mutex. - pmu = &mu->mutex; - if (pthread_mutex_lock (pmu)) - return 1; - - int r = mu->thread != pthread_self () || mu->count == 0; -#endif /* HAVE_RECURSIVE_MUTEX */ - - pthread_mutex_unlock (pmu); - return r; + pthread_t self = pthread_self(); + if (mu->owner == self) + return 0; + else return 1; } // // Condition variables. // -inline void -_Jv_CondInit (_Jv_ConditionVariable_t *cv) -{ - pthread_cond_init (cv, 0); -} - -#ifndef LINUX_THREADS - -// pthread_cond_destroy does nothing on Linux and it is a win to avoid -// defining this macro. - -#define _Jv_HaveCondDestroy - -inline void -_Jv_CondDestroy (_Jv_ConditionVariable_t *cv) -{ - pthread_cond_destroy (cv); -} - -#endif /* LINUX_THREADS */ - int _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint nanos); + +int _Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu); -inline int -_Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) -{ - return _Jv_PthreadCheckMonitor (mu) || pthread_cond_signal (cv); -} +int _Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu); -inline int -_Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) +inline void +_Jv_CondInit (_Jv_ConditionVariable_t *cv) { - return _Jv_PthreadCheckMonitor (mu) || pthread_cond_broadcast (cv); + cv->first = NULL; } - // // Mutexes. // -#ifdef RECURSIVE_MUTEX_IS_DEFAULT inline void _Jv_MutexInit (_Jv_Mutex_t *mu) { - pthread_mutex_init (_Jv_PthreadGetMutex (mu), NULL); -#ifdef PTHREAD_MUTEX_IS_STRUCT + pthread_mutex_init (&mu->mutex, NULL); + mu->count = 0; -#endif + mu->owner = 0; } -#else -void _Jv_MutexInit (_Jv_Mutex_t *mu); -#endif + +int _Jv_MutexLock (_Jv_Mutex_t *mu); +int _Jv_MutexUnlock (_Jv_Mutex_t *mu); #ifndef LINUX_THREADS @@ -201,53 +125,14 @@ void _Jv_MutexInit (_Jv_Mutex_t *mu); #define _Jv_HaveMutexDestroy -#ifdef HAVE_RECURSIVE_MUTEX - -inline void +inline void _Jv_MutexDestroy (_Jv_Mutex_t *mu) { - pthread_mutex_destroy (_Jv_PthreadGetMutex (mu)); + pthread_mutex_destroy (&mu->mutex); } -#else /* HAVE_RECURSIVE_MUTEX */ - -extern void _Jv_MutexDestroy (_Jv_Mutex_t *mu); - -#endif /* HAVE_RECURSIVE_MUTEX */ #endif /* LINUX_THREADS */ -#ifdef HAVE_RECURSIVE_MUTEX - -inline int -_Jv_MutexLock (_Jv_Mutex_t *mu) -{ - int r = pthread_mutex_lock (_Jv_PthreadGetMutex (mu)); -#ifdef PTHREAD_MUTEX_IS_STRUCT - if (! r) - ++mu->count; -#endif - return r; -} - -inline int -_Jv_MutexUnlock (_Jv_Mutex_t *mu) -{ - int r = pthread_mutex_unlock (_Jv_PthreadGetMutex (mu)); -#ifdef PTHREAD_MUTEX_IS_STRUCT - if (! r) - --mu->count; -#endif - return r; -} - -#else /* HAVE_RECURSIVE_MUTEX */ - -extern int _Jv_MutexLock (_Jv_Mutex_t *mu); -extern int _Jv_MutexUnlock (_Jv_Mutex_t *mu); - -#endif /* HAVE_RECURSIVE_MUTEX */ - - // // Thread creation and manipulation. // diff --git a/libjava/include/win32-threads.h b/libjava/include/win32-threads.h index d87fea0..4938d5f 100644 --- a/libjava/include/win32-threads.h +++ b/libjava/include/win32-threads.h @@ -54,13 +54,15 @@ int _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, inline int _Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *) { - return PulseEvent (*cv) ? 0 : GetLastError (); // FIXME: Map error code? + // FIXME: check for mutex ownership? + return PulseEvent (*cv) ? 0 : _JV_NOT_OWNER; // FIXME? } inline int _Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *) { - return PulseEvent (*cv) ? 0 : GetLastError (); // FIXME: Map error code? + // FIXME: check for mutex ownership? + return PulseEvent (*cv) ? 0 : _JV_NOT_OWNER; // FIXME? } // diff --git a/libjava/java/lang/Thread.java b/libjava/java/lang/Thread.java index 3b867a5..a731b0a 100644 --- a/libjava/java/lang/Thread.java +++ b/libjava/java/lang/Thread.java @@ -79,12 +79,11 @@ public class Thread implements Runnable public static boolean interrupted () { - return currentThread().isInterrupted_(); + return currentThread().isInterrupted (true); } - // FIXME: it seems to me that this should be synchronized. // Check the threads interrupted status. Note that this does not clear the - // threads interrupted status (per JDK 1.2 online API documentation). + // thread's interrupted status (per JDK 1.2 online API documentation). public boolean isInterrupted () { return interrupt_flag; @@ -119,11 +118,18 @@ public class Thread implements Runnable private static final native void run_ (Object obj); private final native void finish_ (); - // Convenience method to check and clear the thread's interrupted status. - private boolean isInterrupted_ () + // Check the thread's interrupted status. If clear_flag is true, the + // thread's interrupted status is also cleared. + private boolean isInterrupted (boolean clear_flag) { boolean r = interrupt_flag; - interrupt_flag = false; + if (clear_flag && r) + { + // Only clear the flag if we saw it as set. Otherwise this could + // potentially cause us to miss an interrupt in a race condition, + // because this method is not synchronized. + interrupt_flag = false; + } return r; } @@ -221,6 +227,8 @@ public class Thread implements Runnable data = null; interrupt_flag = false; alive_flag = false; + startable_flag = true; + if (current != null) { daemon_flag = current.isDaemon(); @@ -267,7 +275,8 @@ public class Thread implements Runnable public String toString () { - return "Thread[" + name + "," + priority + "," + group.getName() + "]"; + return "Thread[" + name + "," + priority + "," + + (group == null ? "" : group.getName()) + "]"; } public static native void yield (); @@ -280,6 +289,7 @@ public class Thread implements Runnable private boolean daemon_flag; private boolean interrupt_flag; private boolean alive_flag; + private boolean startable_flag; // Our native data. private RawData data; diff --git a/libjava/java/lang/natObject.cc b/libjava/java/lang/natObject.cc index 47b88a8..25b2966 100644 --- a/libjava/java/lang/natObject.cc +++ b/libjava/java/lang/natObject.cc @@ -198,11 +198,15 @@ java::lang::Object::wait (jlong timeout, jint nanos) if (timeout < 0 || nanos < 0 || nanos > 999999) JvThrow (new IllegalArgumentException); _Jv_SyncInfo *si = (_Jv_SyncInfo *) sync_info; - if (_Jv_CondWait (&si->condition, &si->mutex, timeout, nanos)) - JvThrow (new IllegalMonitorStateException(JvNewStringLatin1 - ("current thread not owner"))); - if (Thread::interrupted()) - JvThrow (new InterruptedException); + switch (_Jv_CondWait (&si->condition, &si->mutex, timeout, nanos)) + { + case _JV_NOT_OWNER: + JvThrow (new IllegalMonitorStateException (JvNewStringLatin1 + ("current thread not owner"))); + case _JV_INTERRUPTED: + if (Thread::interrupted ()) + JvThrow (new InterruptedException); + } } // diff --git a/libjava/java/lang/natThread.cc b/libjava/java/lang/natThread.cc index 6ff548c..9fc30b9 100644 --- a/libjava/java/lang/natThread.cc +++ b/libjava/java/lang/natThread.cc @@ -35,47 +35,16 @@ details. */ struct natThread { // These are used to interrupt sleep and join calls. We can share a - // condition variable here since this thread can either be sleeping - // or waiting for a thread exit, but not both. - _Jv_Mutex_t interrupt_mutex; - _Jv_ConditionVariable_t interrupt_cond; + // condition variable here since it only ever gets notified when the thread + // exits. + _Jv_Mutex_t join_mutex; + _Jv_ConditionVariable_t join_cond; // This is private data for the thread system layer. _Jv_Thread_t *thread; // Each thread has its own JNI object. JNIEnv *jni_env; - - // All threads waiting to join this thread are linked together and - // waiting on their respective `interrupt' condition variables. - // When this thread exits, it notifies each such thread by - // signalling the condition. In this case the `interrupt_flag' is - // not set; this is how the waiting thread knows whether the join - // has failed or whether it should throw an exception. - struct natThread *joiner; - - // Chain for waiters. - struct natThread *next; -}; - -// We use this for its side effects: it lets us lock a mutex directly -// and not lose if an exception is thrown. -class locker -{ -private: - _Jv_Mutex_t *mutex; - -public: - locker (_Jv_Mutex_t *m) - : mutex (m) - { - _Jv_MutexLock (mutex); - } - - ~locker () - { - _Jv_MutexUnlock (mutex); - } }; // This is called from the constructor to initialize the native side @@ -90,14 +59,12 @@ java::lang::Thread::initialize_native (void) // any "interesting" point. natThread *nt = (natThread *) _Jv_AllocBytes (sizeof (natThread)); data = reinterpret_cast (nt); - _Jv_MutexInit (&nt->interrupt_mutex); - _Jv_CondInit (&nt->interrupt_cond); + _Jv_MutexInit (&nt->join_mutex); + _Jv_CondInit (&nt->join_cond); _Jv_ThreadInitData (&nt->thread, this); // FIXME: if JNI_ENV is set we will want to free it. It is // malloc()d. nt->jni_env = NULL; - nt->joiner = 0; - nt->next = 0; } jint @@ -125,90 +92,33 @@ java::lang::Thread::destroy (void) void java::lang::Thread::interrupt (void) { - interrupt_flag = true; - - // Wake up this thread, whether it is sleeping or waiting for - // another thread to exit. natThread *nt = (natThread *) data; - _Jv_MutexLock (&nt->interrupt_mutex); - // Notify the interrupt condition to interrupt sleep() and join() calls. - _Jv_CondNotify (&nt->interrupt_cond, &nt->interrupt_mutex); - // Send a signal to the target thread to interrupt system calls. On Linux, - // this will also interrupt the target thread from *any* _Jv_CondWait call, - // ie wait(). This behaviour is not portable, however. _Jv_ThreadInterrupt (nt->thread); - _Jv_MutexUnlock (&nt->interrupt_mutex); } void java::lang::Thread::join (jlong millis, jint nanos) { - // FIXME: what if we are trying to join ourselves with no timeout? - if (millis < 0 || nanos < 0 || nanos > 999999) _Jv_Throw (new IllegalArgumentException); Thread *current = currentThread (); - if (current->isInterrupted_ ()) - _Jv_Throw (new InterruptedException); - // Update the list of all threads waiting for this thread to exit. - // We grab a mutex when doing this in order to ensure that the - // required state changes are atomic. - _Jv_MonitorEnter (this); - if (! isAlive ()) - { - _Jv_MonitorExit (this); - return; - } - - // Here `CURR_NT' is the native structure for the currently - // executing thread, while `NT' is the native structure for the - // thread we are trying to join. - natThread *curr_nt = (natThread *) current->data; + // Here `NT' is the native structure for the thread we are trying to join. natThread *nt = (natThread *) data; - JvAssert (curr_nt->next == NULL); - // Put thread CURR_NT onto NT's list. When NT exits, it will - // traverse its list and notify all joiners. - curr_nt->next = nt->joiner; - nt->joiner = curr_nt; - _Jv_MonitorExit (this); - - // Now wait for: (1) an interrupt, (2) the thread to exit, or (3) - // the timeout to occur. Use a `locker' object because _Jv_CondWait - // can throw an exception. - { - locker l (&curr_nt->interrupt_mutex); - _Jv_CondWait (&curr_nt->interrupt_cond, - &curr_nt->interrupt_mutex, - millis, nanos); - } - - // Now the join has completed, one way or another. Update the - // joiners list to account for this. - _Jv_MonitorEnter (this); - JvAssert (nt->joiner != NULL); - natThread *prev = 0; - natThread *t; - for (t = nt->joiner; t != NULL; t = t->next) + // the timeout to occur. + _Jv_MutexLock (&nt->join_mutex); + if (! isAlive ()) { - if (t == curr_nt) - { - if (prev) - prev->next = t->next; - else - nt->joiner = t->next; - t->next = 0; - break; - } - prev = t; + _Jv_MutexUnlock (&nt->join_mutex); + return; } - JvAssert (t != NULL); - _Jv_MonitorExit (this); + _Jv_CondWait (&nt->join_cond, &nt->join_mutex, millis, nanos); + _Jv_MutexUnlock (&nt->join_mutex); - if (current->isInterrupted_ ()) + if (current->isInterrupted (true)) _Jv_Throw (new InterruptedException); } @@ -245,43 +155,31 @@ java::lang::Thread::sleep (jlong millis, jint nanos) ++nanos; Thread *current = currentThread (); - if (current->isInterrupted_ ()) - _Jv_Throw (new InterruptedException); // We use a condition variable to implement sleeping so that an - // interrupt can wake us up. + // interrupt can wake us up. natThread *nt = (natThread *) current->data; - { - // Use a locker because _Jv_CondWait can throw an exception. - locker l (&nt->interrupt_mutex); - _Jv_CondWait (&nt->interrupt_cond, &nt->interrupt_mutex, - millis, nanos); - } - - if (current->isInterrupted_ ()) + _Jv_MutexLock (&nt->join_mutex); + _Jv_CondWait (&nt->join_cond, &nt->join_mutex, millis, nanos); + _Jv_MutexUnlock (&nt->join_mutex); + + if (current->isInterrupted (true)) _Jv_Throw (new InterruptedException); } void java::lang::Thread::finish_ () { - // Notify all threads waiting to join this this. - _Jv_MonitorEnter (this); - alive_flag = false; - - // Note that we don't bother cleaning up the joiner list here. That - // is taken care of when each thread wakes up again. natThread *nt = (natThread *) data; - for (natThread *t = nt->joiner; t != NULL; t = t->next) - { - _Jv_MutexLock (&t->interrupt_mutex); - _Jv_CondNotify (&t->interrupt_cond, &t->interrupt_mutex); - _Jv_MutexUnlock (&t->interrupt_mutex); - } - + group->remove (this); - - _Jv_MonitorExit (this); + group = NULL; + + // Signal any threads that are waiting to join() us. + _Jv_MutexLock (&nt->join_mutex); + alive_flag = false; + _Jv_CondNotifyAll (&nt->join_cond, &nt->join_mutex); + _Jv_MutexUnlock (&nt->join_mutex); } void @@ -314,10 +212,12 @@ java::lang::Thread::start (void) { JvSynchronize sync (this); - if (alive_flag) + // Its illegal to re-start() a thread, even if its dead. + if (!startable_flag) _Jv_Throw (new IllegalThreadStateException); alive_flag = true; + startable_flag = false; natThread *nt = (natThread *) data; _Jv_ThreadStart (this, nt->thread, (_Jv_ThreadStartFunc *) &run_); } diff --git a/libjava/posix-threads.cc b/libjava/posix-threads.cc index cf29a25..a664ee3 100644 --- a/libjava/posix-threads.cc +++ b/libjava/posix-threads.cc @@ -40,7 +40,6 @@ extern "C" struct starter { _Jv_ThreadStartFunc *method; - java::lang::Thread *object; _Jv_Thread_t *data; }; @@ -78,30 +77,23 @@ static int non_daemon_count; +// Wait for the condition variable "CV" to be notified. +// Return values: +// 0: the condition was notified, or the timeout expired. +// _JV_NOT_OWNER: the thread does not own the mutex "MU". +// _JV_INTERRUPTED: the thread was interrupted. Its interrupted flag is set. int _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint nanos) { - if (_Jv_PthreadCheckMonitor (mu)) - return 1; + pthread_t self = pthread_self(); + if (mu->owner != self) + return _JV_NOT_OWNER; - int r; - pthread_mutex_t *pmu = _Jv_PthreadGetMutex (mu); struct timespec ts; - jlong m, m2, startTime; - bool done_sleeping = false; + jlong m, startTime; - if (millis == 0 && nanos == 0) - { -#ifdef LINUX_THREADS - // pthread_cond_timedwait can be interrupted by a signal on linux, while - // pthread_cond_wait can not. So pthread_cond_timedwait() forever. - m = java::lang::Long::MAX_VALUE; - ts.tv_sec = LONG_MAX; - ts.tv_nsec = 0; -#endif - } - else + if (millis > 0 || nanos > 0) { startTime = java::lang::System::currentTimeMillis(); m = millis + startTime; @@ -109,172 +101,178 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, ts.tv_nsec = ((m % 1000) * 1000000) + nanos; } - java::lang::Thread *current = _Jv_ThreadCurrent(); + _Jv_Thread_t *current = _Jv_ThreadCurrentData (); + java::lang::Thread *current_obj = _Jv_ThreadCurrent (); + + // Add this thread to the cv's wait set. + current->next = NULL; - do + if (cv->first == NULL) + cv->first = current; + else + for (_Jv_Thread_t *t = cv->first;; t = t->next) + { + if (t->next == NULL) + { + t->next = current; + break; + } + } + + pthread_mutex_lock (¤t->wait_mutex); + + // Now that we hold the wait mutex, check if this thread has been + // interrupted already. + if (current_obj->interrupt_flag) { - r = EINTR; - // Check to ensure the thread hasn't already been interrupted. - if (!(current->isInterrupted ())) + pthread_mutex_unlock (¤t->wait_mutex); + return _JV_INTERRUPTED; + } + + // Record the current lock depth, so it can be restored when we re-aquire it. + int count = mu->count; + + // Release the monitor mutex. + mu->count = 0; + mu->owner = 0; + pthread_mutex_unlock (&mu->mutex); + + int r = 0; + bool done_sleeping = false; + + while (! done_sleeping) + { + if (millis == 0 && nanos == 0) + r = pthread_cond_wait (¤t->wait_cond, ¤t->wait_mutex); + else + r = pthread_cond_timedwait (¤t->wait_cond, ¤t->wait_mutex, + &ts); + + // In older glibc's (prior to 2.1.3), the cond_wait functions may + // spuriously wake up on a signal. Catch that here. + if (r != EINTR) + done_sleeping = true; + } + + // Check for an interrupt *before* unlocking the wait mutex. + jboolean interrupted = current_obj->interrupt_flag; + + pthread_mutex_unlock (¤t->wait_mutex); + + // Reaquire the monitor mutex, and restore the lock count. + pthread_mutex_lock (&mu->mutex); + mu->owner = self; + mu->count = count; + + // If we were interrupted, or if a timeout occured, remove ourself from + // the cv wait list now. (If we were notified normally, notify() will have + // already taken care of this) + if (r == ETIMEDOUT || interrupted) + { + _Jv_Thread_t *prev = NULL; + for (_Jv_Thread_t *t = cv->first; t != NULL; t = t->next) { -#ifdef LINUX_THREADS - // FIXME: in theory, interrupt() could be called on this thread - // between the test above and the wait below, resulting in the - // interupt() call failing. I don't see a way to fix this - // without significant changes to the implementation. - r = pthread_cond_timedwait (cv, pmu, &ts); -#else - if (millis == 0 && nanos == 0) - r = pthread_cond_wait (cv, pmu); - else - r = pthread_cond_timedwait (cv, pmu, &ts); -#endif - } - - if (r == EINTR) - { - /* We were interrupted by a signal. Either this is - because we were interrupted intentionally (i.e. by - Thread.interrupt()) or by the GC if it is - signal-based. */ - if (current->isInterrupted ()) + if (t == current) { - r = 0; - done_sleeping = true; - } - else - { - /* We were woken up by the GC or another signal. */ - m2 = java::lang::System::currentTimeMillis (); - if (m2 >= m) - { - r = 0; - done_sleeping = true; - } + if (prev != NULL) + prev->next = t->next; + else + cv->first = t->next; + t->next = NULL; + break; } + prev = t; } - else if (r == ETIMEDOUT) - { - /* A timeout is a normal result. */ - r = 0; - done_sleeping = true; - } - else - done_sleeping = true; + if (interrupted) + return _JV_INTERRUPTED; } - while (! done_sleeping); - - return r != 0; + + return 0; } -#ifndef RECURSIVE_MUTEX_IS_DEFAULT - -void -_Jv_MutexInit (_Jv_Mutex_t *mu) +int +_Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) { -#ifdef HAVE_RECURSIVE_MUTEX - pthread_mutexattr_t *val = NULL; - -#if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE) - pthread_mutexattr_t attr; - - // If this is slow, then allocate it statically and only initialize - // it once. - pthread_mutexattr_init (&attr); - pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); - val = &attr; -#elif defined (HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) - pthread_mutexattr_t attr; - pthread_mutexattr_init (&attr); - pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_RECURSIVE_NP); - val = &attr; -#endif - - pthread_mutex_init (_Jv_PthreadGetMutex (mu), val); -#ifdef PTHREAD_MUTEX_IS_STRUCT - mu->count = 0; -#endif - -#if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE) || defined (HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) - pthread_mutexattr_destroy (&attr); -#endif + if (_Jv_PthreadCheckMonitor (mu)) + return _JV_NOT_OWNER; -#else /* HAVE_RECURSIVE_MUTEX */ + _Jv_Thread_t *target; + _Jv_Thread_t *prev = NULL; - // No recursive mutex, so simulate one. - pthread_mutex_init (&mu->mutex, NULL); - pthread_mutex_init (&mu->mutex2, NULL); - pthread_cond_init (&mu->cond, 0); - mu->count = 0; + for (target = cv->first; target != NULL; target = target->next) + { + pthread_mutex_lock (&target->wait_mutex); -#endif /* HAVE_RECURSIVE_MUTEX */ -} + if (target->thread_obj->interrupt_flag) + { + // Don't notify a thread that has already been interrupted. + pthread_mutex_unlock (&target->wait_mutex); + prev = target; + continue; + } -#endif /* not RECURSIVE_MUTEX_IS_DEFAULT */ + pthread_cond_signal (&target->wait_cond); + pthread_mutex_unlock (&target->wait_mutex); -#if ! defined (LINUX_THREADS) && ! defined (HAVE_RECURSIVE_MUTEX) + // Two successive notify() calls should not be delivered to the same + // thread, so we remove the target thread from the cv wait list now. + if (prev == NULL) + cv->first = target->next; + else + prev->next = target->next; + + target->next = NULL; + + break; + } -void -_Jv_MutexDestroy (_Jv_Mutex_t *mu) -{ - pthread_mutex_destroy (&mu->mutex); - pthread_mutex_destroy (&mu->mutex2); - pthread_cond_destroy (&mu->cond); + return 0; } int -_Jv_MutexLock (_Jv_Mutex_t *mu) +_Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) { - if (pthread_mutex_lock (&mu->mutex)) - return -1; - while (1) + if (_Jv_PthreadCheckMonitor (mu)) + return _JV_NOT_OWNER; + + _Jv_Thread_t *target; + _Jv_Thread_t *prev = NULL; + + for (target = cv->first; target != NULL; target = target->next) { - if (mu->count == 0) - { - // Grab the lock. - mu->thread = pthread_self (); - mu->count = 1; - pthread_mutex_lock (&mu->mutex2); - break; - } - else if (pthread_self () == mu->thread) - { - // Already have the lock. - mu->count += 1; - break; - } - else - { - // Try to acquire the lock. - pthread_cond_wait (&mu->cond, &mu->mutex); - } + pthread_mutex_lock (&target->wait_mutex); + pthread_cond_signal (&target->wait_cond); + pthread_mutex_unlock (&target->wait_mutex); + + if (prev != NULL) + prev->next = NULL; + prev = target; } - pthread_mutex_unlock (&mu->mutex); + if (prev != NULL) + prev->next = NULL; + + cv->first = NULL; + return 0; } -int -_Jv_MutexUnlock (_Jv_Mutex_t *mu) +void +_Jv_ThreadInterrupt (_Jv_Thread_t *data) { - if (pthread_mutex_lock (&mu->mutex)) - return -1; - int r = 0; - if (mu->count == 0 || pthread_self () != mu->thread) - r = -1; - else - { - mu->count -= 1; - if (! mu->count) - { - pthread_mutex_unlock (&mu->mutex2); - pthread_cond_signal (&mu->cond); - } - } - pthread_mutex_unlock (&mu->mutex); - return r; -} + pthread_mutex_lock (&data->wait_mutex); -#endif /* not LINUX_THREADS and not HAVE_RECURSIVE_MUTEX */ + // Set the thread's interrupted flag *after* aquiring its wait_mutex. This + // ensures that there are no races with the interrupt flag being set after + // the waiting thread checks it and before pthread_cond_wait is entered. + data->thread_obj->interrupt_flag = true; + + // Interrupt blocking system calls using a signal. +// pthread_kill (data->thread, INTR); + + pthread_cond_signal (&data->wait_cond); + + pthread_mutex_unlock (&data->wait_mutex); +} static void handle_intr (int) @@ -300,10 +298,14 @@ _Jv_InitThreads (void) } void -_Jv_ThreadInitData (_Jv_Thread_t **data, java::lang::Thread *) +_Jv_ThreadInitData (_Jv_Thread_t **data, java::lang::Thread *obj) { _Jv_Thread_t *info = new _Jv_Thread_t; info->flags = 0; + info->thread_obj = obj; + + pthread_mutex_init (&info->wait_mutex, NULL); + pthread_cond_init (&info->wait_cond, NULL); // FIXME register a finalizer for INFO here. // FIXME also must mark INFO somehow. @@ -331,10 +333,16 @@ really_start (void *x) { struct starter *info = (struct starter *) x; - pthread_setspecific (_Jv_ThreadKey, info->object); + pthread_setspecific (_Jv_ThreadKey, info->data->thread_obj); pthread_setspecific (_Jv_ThreadDataKey, info->data); - info->method (info->object); + // glibc 2.1.3 doesn't set the value of `thread' until after start_routine + // is called. Since it may need to be accessed from the new thread, work + // around the potential race here by explicitly setting it again. + info->data->thread = pthread_self (); + + info->method (info->data->thread_obj); + if (! (info->data->flags & FLAG_DAEMON)) { pthread_mutex_lock (&daemon_mutex); @@ -343,6 +351,12 @@ really_start (void *x) pthread_cond_signal (&daemon_cond); pthread_mutex_unlock (&daemon_mutex); } + +#ifndef LINUX_THREADS + // Clean up. These calls do nothing on Linux. + pthread_mutex_destroy (&info->data->wait_mutex); + pthread_cond_destroy (&info->data->wait_cond); +#endif /* ! LINUX_THREADS */ return NULL; } @@ -367,7 +381,6 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, // FIXME: handle marking the info object for GC. info = (struct starter *) _Jv_AllocBytes (sizeof (struct starter)); info->method = meth; - info->object = thread; info->data = data; if (! thread->isDaemon()) @@ -389,6 +402,39 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, } } +int +_Jv_MutexLock (_Jv_Mutex_t *mu) +{ + pthread_t self = pthread_self (); + if (mu->owner == self) + { + mu->count++; + } + else + { + pthread_mutex_lock (&mu->mutex); + mu->count = 1; + mu->owner = self; + } + return 0; +} + +int +_Jv_MutexUnlock (_Jv_Mutex_t *mu) +{ + if (_Jv_PthreadCheckMonitor (mu)) + return _JV_NOT_OWNER; + + mu->count--; + + if (mu->count == 0) + { + mu->owner = 0; + pthread_mutex_unlock (&mu->mutex); + } + return 0; +} + void _Jv_ThreadWait (void) { @@ -397,9 +443,3 @@ _Jv_ThreadWait (void) pthread_cond_wait (&daemon_cond, &daemon_mutex); pthread_mutex_unlock (&daemon_mutex); } - -void -_Jv_ThreadInterrupt (_Jv_Thread_t *data) -{ - pthread_kill (data->thread, INTR); -} diff --git a/libjava/win32-threads.cc b/libjava/win32-threads.cc index 9c6ae8c..8da274f 100644 --- a/libjava/win32-threads.cc +++ b/libjava/win32-threads.cc @@ -69,6 +69,8 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint n DWORD time; DWORD rval; + // FIXME: check for mutex ownership? + _Jv_MutexUnlock (mu); if((millis == 0) && (nanos > 0)) @@ -82,9 +84,7 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint n _Jv_MutexLock (mu); if (rval == WAIT_FAILED) - return GetLastError (); // FIXME: Map to errno? - else if (rval == WAIT_TIMEOUT) - return ETIMEDOUT; + return _JV_NOT_OWNER; // FIXME? else return 0; } -- cgit v1.1