X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fposix-threads.cc;h=91da25cf38de9d9fe49557993aa7a30b33b941f5;hb=7bb1225a6a31c86f74dfd7284e10d8bac72e17cb;hp=7e721c57711eb021c07fd5904e3a12c96a4e6222;hpb=f1ea3dc717906eaf2ebcf4d1251312099636cc08;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/posix-threads.cc b/libjava/posix-threads.cc index 7e721c57711..91da25cf38d 100644 --- a/libjava/posix-threads.cc +++ b/libjava/posix-threads.cc @@ -1,6 +1,6 @@ // posix-threads.cc - interface between libjava and POSIX threads. -/* Copyright (C) 1998, 1999 Cygnus Solutions +/* Copyright (C) 1998, 1999, 2000, 2001, 2004 Free Software Foundation This file is part of libgcj. @@ -16,27 +16,30 @@ details. */ // If we're using the Boehm GC, then we need to override some of the // thread primitives. This is fairly gross. #ifdef HAVE_BOEHM_GC -extern "C" -{ -#include #include -}; #endif /* HAVE_BOEHM_GC */ #include #include #include +#include +#include +#ifdef HAVE_UNISTD_H +#include // To test for _POSIX_THREAD_PRIORITY_SCHEDULING +#endif -#include +#include #include #include #include +#include +#include +#include // This is used to implement thread startup. struct starter { _Jv_ThreadStartFunc *method; - java::lang::Thread *object; _Jv_Thread_t *data; }; @@ -45,6 +48,10 @@ struct starter // threads, so it is ok to make it a global here. pthread_key_t _Jv_ThreadKey; +// This is the key used to map from the POSIX thread value back to the +// _Jv_Thread_t* representing the thread. +pthread_key_t _Jv_ThreadDataKey; + // We keep a count of all non-daemon threads which are running. When // this reaches zero, _Jv_ThreadWait returns. static pthread_mutex_t daemon_mutex; @@ -52,8 +59,9 @@ static pthread_cond_t daemon_cond; static int non_daemon_count; // The signal to use when interrupting a thread. -#ifdef LINUX_THREADS - // LinuxThreads usurps both SIGUSR1 and SIGUSR2. +#if defined(LINUX_THREADS) || defined(FREEBSD_THREADS) + // LinuxThreads (prior to glibc 2.1) usurps both SIGUSR1 and SIGUSR2. + // GC on FreeBSD uses both SIGUSR1 and SIGUSR2. # define INTR SIGHUP #else /* LINUX_THREADS */ # define INTR SIGUSR2 @@ -70,139 +78,202 @@ 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) { - int r; - pthread_mutex_t *pmu; -#ifdef HAVE_RECURSIVE_MUTEX - pmu = mu; -#else - pmu = &mu->mutex2; -#endif - if (millis == 0 && nanos == 0) - r = pthread_cond_wait (cv, pmu); - else + pthread_t self = pthread_self(); + if (mu->owner != self) + return _JV_NOT_OWNER; + + struct timespec ts; + jlong m, startTime; + + if (millis > 0 || nanos > 0) { - struct timespec ts; - jlong m = millis + java::lang::System::currentTimeMillis (); + startTime = java::lang::System::currentTimeMillis(); + m = millis + startTime; ts.tv_sec = m / 1000; ts.tv_nsec = ((m % 1000) * 1000000) + nanos; - - r = pthread_cond_timedwait (cv, pmu, &ts); - /* A timeout is a normal result. */ - if (r && errno == ETIMEDOUT) - r = 0; } - return r; -} - -#ifndef RECURSIVE_MUTEX_IS_DEFAULT -void -_Jv_MutexInit (_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 + _Jv_Thread_t *current = _Jv_ThreadCurrentData (); + java::lang::Thread *current_obj = _Jv_ThreadCurrent (); - pthread_mutex_init (mu, val); + pthread_mutex_lock (¤t->wait_mutex); -#if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE) || defined (HAVE_PTHREAD_MUTEXATTR_SETKIND_NP) - pthread_mutexattr_destroy (&attr); -#endif + // Now that we hold the wait mutex, check if this thread has been + // interrupted already. + if (current_obj->interrupt_flag) + { + pthread_mutex_unlock (¤t->wait_mutex); + return _JV_INTERRUPTED; + } -#else /* HAVE_RECURSIVE_MUTEX */ + // Add this thread to the cv's wait set. + current->next = 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); + 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; + } + } + + // 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; -#endif /* HAVE_RECURSIVE_MUTEX */ -} - -#endif /* not RECURSIVE_MUTEX_IS_DEFAULT */ - -#if ! defined (LINUX_THREADS) && ! defined (HAVE_RECURSIVE_MUTEX) + 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); -void -_Jv_MutexDestroy (_Jv_Mutex_t *mu) -{ - pthread_mutex_destroy (&mu->mutex); - pthread_mutex_destroy (&mu->mutex2); - pthread_cond_destroy (&mu->cond); + // 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* releasing 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 occurred, 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) + { + if (t == current) + { + if (prev != NULL) + prev->next = t->next; + else + cv->first = t->next; + t->next = NULL; + break; + } + prev = t; + } + if (interrupted) + return _JV_INTERRUPTED; + } + + return 0; } int -_Jv_MutexLock (_Jv_Mutex_t *mu) +_Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) { - if (pthread_mutex_lock (&mu->mutex)) - return -1; - while (1) + if (_Jv_MutexCheckMonitor (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; + pthread_mutex_lock (&target->wait_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; } + + pthread_cond_signal (&target->wait_cond); + pthread_mutex_unlock (&target->wait_mutex); + + // Two concurrent notify() calls must not be delivered to the same + // thread, so remove the target thread from the cv wait list now. + if (prev == NULL) + cv->first = target->next; else - { - // Try to acquire the lock. - pthread_cond_wait (&mu->cond, &mu->mutex); - } + prev->next = target->next; + + target->next = NULL; + + break; } - pthread_mutex_unlock (&mu->mutex); + return 0; } int -_Jv_MutexUnlock (_Jv_Mutex_t *mu) +_Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) { - if (pthread_mutex_lock (&mu->mutex)) - return -1; - int r = 0; - if (mu->count == 0 || pthread_self () != mu->thread) - r = -1; - else + if (_Jv_MutexCheckMonitor (mu)) + return _JV_NOT_OWNER; + + _Jv_Thread_t *target; + _Jv_Thread_t *prev = NULL; + + for (target = cv->first; target != NULL; target = target->next) { - mu->count -= 1; - if (! mu->count) - { - pthread_mutex_unlock (&mu->mutex2); - pthread_cond_signal (&mu->cond); - } + 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); - return r; + if (prev != NULL) + prev->next = NULL; + + cv->first = NULL; + + return 0; } -#endif /* not LINUX_THREADS and not HAVE_RECURSIVE_MUTEX */ +void +_Jv_ThreadInterrupt (_Jv_Thread_t *data) +{ + pthread_mutex_lock (&data->wait_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) @@ -210,10 +281,22 @@ handle_intr (int) // Do nothing. } +static void +block_sigchld() +{ + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + int c = pthread_sigmask (SIG_BLOCK, &mask, NULL); + if (c != 0) + JvFail (strerror (c)); +} + void _Jv_InitThreads (void) { pthread_key_create (&_Jv_ThreadKey, NULL); + pthread_key_create (&_Jv_ThreadDataKey, NULL); pthread_mutex_init (&daemon_mutex, NULL); pthread_cond_init (&daemon_cond, 0); non_daemon_count = 0; @@ -225,31 +308,36 @@ _Jv_InitThreads (void) act.sa_flags = 0; sigaction (INTR, &act, NULL); - // Arrange for SIGINT to be blocked to all threads. It is only - // deliverable to the master thread. - sigset_t mask; - sigemptyset (&mask); - sigaddset (&mask, SIGINT); - pthread_sigmask (SIG_BLOCK, &mask, NULL); + // Block SIGCHLD here to ensure that any non-Java threads inherit the new + // signal mask. + block_sigchld(); } -void -_Jv_ThreadInitData (_Jv_Thread_t **data, java::lang::Thread *) +_Jv_Thread_t * +_Jv_ThreadInitData (java::lang::Thread *obj) { - _Jv_Thread_t *info = new _Jv_Thread_t; + _Jv_Thread_t *data = (_Jv_Thread_t *) _Jv_Malloc (sizeof (_Jv_Thread_t)); + data->flags = 0; + data->thread_obj = obj; - info->flags = 0; - info->exception = NULL; + pthread_mutex_init (&data->wait_mutex, NULL); + pthread_cond_init (&data->wait_cond, NULL); - // FIXME register a finalizer for INFO here. - // FIXME also must mark INFO somehow. + return data; +} - *data = info; +void +_Jv_ThreadDestroyData (_Jv_Thread_t *data) +{ + pthread_mutex_destroy (&data->wait_mutex); + pthread_cond_destroy (&data->wait_cond); + _Jv_Free ((void *)data); } void _Jv_ThreadSetPriority (_Jv_Thread_t *data, jint prio) { +#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING if (data->flags & FLAG_START) { struct sched_param param; @@ -257,26 +345,44 @@ _Jv_ThreadSetPriority (_Jv_Thread_t *data, jint prio) param.sched_priority = prio; pthread_setschedparam (data->thread, SCHED_RR, ¶m); } +#endif } - -// This is called as a cleanup handler when a thread is exiting. We -// use it to throw the requested exception. It's entirely possible -// that this approach is doomed to failure, in which case we'll need -// to adopt some alternate. For instance, use a signal to implement -// _Jv_ThreadCancel. -static void -throw_cleanup (void *data) +void +_Jv_ThreadRegister (_Jv_Thread_t *data) { - _Jv_Thread_t *td = (_Jv_Thread_t *) data; - _Jv_Throw ((java::lang::Throwable *) td->exception); + pthread_setspecific (_Jv_ThreadKey, data->thread_obj); + pthread_setspecific (_Jv_ThreadDataKey, data); + + // 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. + data->thread = pthread_self (); + +# ifdef SLOW_PTHREAD_SELF + // Clear all self cache slots that might be needed by this thread. + int dummy; + int low_index = SC_INDEX(&dummy) + SC_CLEAR_MIN; + int high_index = SC_INDEX(&dummy) + SC_CLEAR_MAX; + for (int i = low_index; i <= high_index; ++i) + { + int current_index = i; + if (current_index < 0) + current_index += SELF_CACHE_SIZE; + if (current_index >= SELF_CACHE_SIZE) + current_index -= SELF_CACHE_SIZE; + _Jv_self_cache[current_index].high_sp_bits = BAD_HIGH_SP_VALUE; + } +# endif + // Block SIGCHLD which is used in natPosixProcess.cc. + block_sigchld(); } void -_Jv_ThreadCancel (_Jv_Thread_t *data, void *error) +_Jv_ThreadUnRegister () { - data->exception = error; - pthread_cancel (data->thread); + pthread_setspecific (_Jv_ThreadKey, NULL); + pthread_setspecific (_Jv_ThreadDataKey, NULL); } // This function is called when a thread is started. We don't arrange @@ -287,10 +393,9 @@ really_start (void *x) { struct starter *info = (struct starter *) x; - pthread_cleanup_push (throw_cleanup, info->data); - pthread_setspecific (_Jv_ThreadKey, info->object); - info->method (info->object); - pthread_cleanup_pop (0); + _Jv_ThreadRegister (info->data); + + info->method (info->data->thread_obj); if (! (info->data->flags & FLAG_DAEMON)) { @@ -316,15 +421,18 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, return; data->flags |= FLAG_START; + // Block SIGCHLD which is used in natPosixProcess.cc. + // The current mask is inherited by the child thread. + block_sigchld(); + param.sched_priority = thread->getPriority(); pthread_attr_init (&attr); pthread_attr_setschedparam (&attr, ¶m); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); - // 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()) @@ -335,28 +443,41 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, } else data->flags |= FLAG_DAEMON; - pthread_create (&data->thread, &attr, really_start, (void *) info); - + int r = pthread_create (&data->thread, &attr, really_start, (void *) info); + pthread_attr_destroy (&attr); + + if (r) + { + const char* msg = "Cannot create additional threads"; + throw new java::lang::OutOfMemoryError (JvNewStringUTF (msg)); + } } void _Jv_ThreadWait (void) { - // Arrange for SIGINT to be delivered to the master thread. - sigset_t mask; - sigemptyset (&mask); - sigaddset (&mask, SIGINT); - pthread_sigmask (SIG_UNBLOCK, &mask, NULL); - pthread_mutex_lock (&daemon_mutex); if (non_daemon_count) pthread_cond_wait (&daemon_cond, &daemon_mutex); pthread_mutex_unlock (&daemon_mutex); } -void -_Jv_ThreadInterrupt (_Jv_Thread_t *data) +#if defined(SLOW_PTHREAD_SELF) + +#include "sysdep/locks.h" + +// Support for pthread_self() lookup cache. +volatile self_cache_entry _Jv_self_cache[SELF_CACHE_SIZE]; + +_Jv_ThreadId_t +_Jv_ThreadSelf_out_of_line(volatile self_cache_entry *sce, size_t high_sp_bits) { - pthread_kill (data->thread, INTR); + pthread_t self = pthread_self(); + sce -> high_sp_bits = high_sp_bits; + write_barrier(); + sce -> self = self; + return self; } + +#endif /* SLOW_PTHREAD_SELF */