X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=libjava%2Fposix-threads.cc;h=66693abbc9b5fbbf44052ba567b0d16e658c7178;hb=3cbe17f7ded6cbdf171c9e7368b0c391501507f1;hp=e5a1668dac8579bc714ba79f11e2543c383d749d;hpb=f52c723967006d4c135e46bb3e2974e54657a5d8;p=gcc.git diff --git a/libjava/posix-threads.cc b/libjava/posix-threads.cc index e5a1668dac8..66693abbc9b 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 Free Software Foundation +/* Copyright (C) 1998, 1999, 2000, 2001, 2004, 2006 Free Software Foundation This file is part of libgcj. @@ -13,14 +13,13 @@ details. */ #include +#include "posix.h" +#include "posix-threads.h" + // 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 @@ -28,6 +27,9 @@ extern "C" #include #include #include +#ifdef HAVE_UNISTD_H +#include // To test for _POSIX_THREAD_PRIORITY_SCHEDULING +#endif #include #include @@ -35,6 +37,7 @@ extern "C" #include #include #include +#include // This is used to implement thread startup. struct starter @@ -59,8 +62,9 @@ static pthread_cond_t daemon_cond; static int non_daemon_count; // The signal to use when interrupting a thread. -#ifdef LINUX_THREADS +#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 @@ -77,6 +81,34 @@ static int non_daemon_count; +int +_Jv_MutexLock (_Jv_Mutex_t *mu) +{ + pthread_t self = pthread_self (); + if (mu->owner == self) + { + mu->count++; + } + else + { + JvSetThreadState holder (_Jv_ThreadCurrent(), JV_BLOCKED); + +# ifdef LOCK_DEBUG + int result = pthread_mutex_lock (&mu->mutex); + if (0 != result) + { + fprintf(stderr, "Pthread_mutex_lock returned %d\n", result); + for (;;) {} + } +# else + pthread_mutex_lock (&mu->mutex); +# endif + mu->count = 1; + mu->owner = self; + } + return 0; +} + // Wait for the condition variable "CV" to be notified. // Return values: // 0: the condition was notified, or the timeout expired. @@ -91,19 +123,63 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, return _JV_NOT_OWNER; struct timespec ts; - jlong m, startTime; + JvThreadState new_state = JV_WAITING; if (millis > 0 || nanos > 0) { - startTime = java::lang::System::currentTimeMillis(); - m = millis + startTime; - ts.tv_sec = m / 1000; - ts.tv_nsec = ((m % 1000) * 1000000) + nanos; + // Calculate the abstime corresponding to the timeout. + unsigned long long seconds; + unsigned long usec; + + // For better accuracy, should use pthread_condattr_setclock + // and clock_gettime. +#ifdef HAVE_GETTIMEOFDAY + timeval tv; + gettimeofday (&tv, NULL); + usec = tv.tv_usec; + seconds = tv.tv_sec; +#else + unsigned long long startTime = java::lang::System::currentTimeMillis(); + seconds = startTime / 1000; + /* Assume we're about half-way through this millisecond. */ + usec = (startTime % 1000) * 1000 + 500; +#endif + /* These next two statements cannot overflow. */ + usec += nanos / 1000; + usec += (millis % 1000) * 1000; + /* These two statements could overflow only if tv.tv_sec was + insanely large. */ + seconds += millis / 1000; + seconds += usec / 1000000; + + ts.tv_sec = seconds; + if (ts.tv_sec < 0 || (unsigned long long)ts.tv_sec != seconds) + { + // We treat a timeout that won't fit into a struct timespec + // as a wait forever. + millis = nanos = 0; + } + else + /* This next statement also cannot overflow. */ + ts.tv_nsec = (usec % 1000000) * 1000 + (nanos % 1000); } _Jv_Thread_t *current = _Jv_ThreadCurrentData (); java::lang::Thread *current_obj = _Jv_ThreadCurrent (); + 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) + { + pthread_mutex_unlock (¤t->wait_mutex); + return _JV_INTERRUPTED; + } + + // Set the thread's state. + JvSetThreadState holder (current_obj, new_state); + // Add this thread to the cv's wait set. current->next = NULL; @@ -119,16 +195,6 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, } } - 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) - { - 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; @@ -147,14 +213,14 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, 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. + // Check for an interrupt *before* releasing the wait mutex. jboolean interrupted = current_obj->interrupt_flag; pthread_mutex_unlock (¤t->wait_mutex); @@ -164,7 +230,7 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, mu->owner = self; mu->count = count; - // If we were interrupted, or if a timeout occured, remove ourself from + // 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) @@ -193,7 +259,7 @@ _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, int _Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) { - if (_Jv_PthreadCheckMonitor (mu)) + if (_Jv_MutexCheckMonitor (mu)) return _JV_NOT_OWNER; _Jv_Thread_t *target; @@ -232,7 +298,7 @@ _Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) int _Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) { - if (_Jv_PthreadCheckMonitor (mu)) + if (_Jv_MutexCheckMonitor (mu)) return _JV_NOT_OWNER; _Jv_Thread_t *target; @@ -267,19 +333,191 @@ _Jv_ThreadInterrupt (_Jv_Thread_t *data) data->thread_obj->interrupt_flag = true; // Interrupt blocking system calls using a signal. -// pthread_kill (data->thread, INTR); + pthread_kill (data->thread, INTR); pthread_cond_signal (&data->wait_cond); pthread_mutex_unlock (&data->wait_mutex); } +/** + * Releases the block on a thread created by _Jv_ThreadPark(). This + * method can also be used to terminate a blockage caused by a prior + * call to park. This operation is unsafe, as the thread must be + * guaranteed to be live. + * + * @param thread the thread to unblock. + */ +void +ParkHelper::unpark () +{ + using namespace ::java::lang; + volatile obj_addr_t *ptr = &permit; + + /* If this thread is in state RUNNING, give it a permit and return + immediately. */ + if (compare_and_swap + (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PERMIT)) + return; + + /* If this thread is parked, put it into state RUNNING and send it a + signal. */ + if (compare_and_swap + (ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING)) + { + int result; + pthread_mutex_lock (&mutex); + result = pthread_cond_signal (&cond); + pthread_mutex_unlock (&mutex); + JvAssert (result == 0); + } +} + +/** + * Sets our state to dead. + */ +void +ParkHelper::deactivate () +{ + permit = ::java::lang::Thread::THREAD_PARK_DEAD; +} + +void +ParkHelper::init () +{ + pthread_mutex_init (&mutex, NULL); + pthread_cond_init (&cond, NULL); + permit = ::java::lang::Thread::THREAD_PARK_RUNNING; +} + +/** + * Blocks the thread until a matching _Jv_ThreadUnpark() occurs, the + * thread is interrupted or the optional timeout expires. If an + * unpark call has already occurred, this also counts. A timeout + * value of zero is defined as no timeout. When isAbsolute is true, + * the timeout is in milliseconds relative to the epoch. Otherwise, + * the value is the number of nanoseconds which must occur before + * timeout. This call may also return spuriously (i.e. for no + * apparent reason). + * + * @param isAbsolute true if the timeout is specified in milliseconds from + * the epoch. + * @param time either the number of nanoseconds to wait, or a time in + * milliseconds from the epoch to wait for. + */ +void +ParkHelper::park (jboolean isAbsolute, jlong time) +{ + using namespace ::java::lang; + volatile obj_addr_t *ptr = &permit; + + /* If we have a permit, return immediately. */ + if (compare_and_swap + (ptr, Thread::THREAD_PARK_PERMIT, Thread::THREAD_PARK_RUNNING)) + return; + + struct timespec ts; + + if (time) + { + unsigned long long seconds; + unsigned long usec; + + if (isAbsolute) + { + ts.tv_sec = time / 1000; + ts.tv_nsec = (time % 1000) * 1000 * 1000; + } + else + { + // Calculate the abstime corresponding to the timeout. + jlong nanos = time; + jlong millis = 0; + + // For better accuracy, should use pthread_condattr_setclock + // and clock_gettime. +#ifdef HAVE_GETTIMEOFDAY + timeval tv; + gettimeofday (&tv, NULL); + usec = tv.tv_usec; + seconds = tv.tv_sec; +#else + unsigned long long startTime + = java::lang::System::currentTimeMillis(); + seconds = startTime / 1000; + /* Assume we're about half-way through this millisecond. */ + usec = (startTime % 1000) * 1000 + 500; +#endif + /* These next two statements cannot overflow. */ + usec += nanos / 1000; + usec += (millis % 1000) * 1000; + /* These two statements could overflow only if tv.tv_sec was + insanely large. */ + seconds += millis / 1000; + seconds += usec / 1000000; + + ts.tv_sec = seconds; + if (ts.tv_sec < 0 || (unsigned long long)ts.tv_sec != seconds) + { + // We treat a timeout that won't fit into a struct timespec + // as a wait forever. + millis = nanos = 0; + } + else + /* This next statement also cannot overflow. */ + ts.tv_nsec = (usec % 1000000) * 1000 + (nanos % 1000); + } + } + + pthread_mutex_lock (&mutex); + if (compare_and_swap + (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PARKED)) + { + int result = 0; + + if (! time) + result = pthread_cond_wait (&cond, &mutex); + else + result = pthread_cond_timedwait (&cond, &mutex, &ts); + + JvAssert (result == 0 || result == ETIMEDOUT); + + /* If we were unparked by some other thread, this will already + be in state THREAD_PARK_RUNNING. If we timed out or were + interrupted, we have to do it ourself. */ + permit = Thread::THREAD_PARK_RUNNING; + } + pthread_mutex_unlock (&mutex); +} + static void handle_intr (int) { // Do nothing. } +void +_Jv_BlockSigchld() +{ + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + int c = pthread_sigmask (SIG_BLOCK, &mask, NULL); + if (c != 0) + JvFail (strerror (c)); +} + +void +_Jv_UnBlockSigchld() +{ + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + int c = pthread_sigmask (SIG_UNBLOCK, &mask, NULL); + if (c != 0) + JvFail (strerror (c)); +} + void _Jv_InitThreads (void) { @@ -295,34 +533,97 @@ _Jv_InitThreads (void) sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction (INTR, &act, NULL); + + // Block SIGCHLD here to ensure that any non-Java threads inherit the new + // signal mask. + _Jv_BlockSigchld(); + + // Check/set the thread stack size. + size_t min_ss = 32 * 1024; + + if (sizeof (void *) == 8) + // Bigger default on 64-bit systems. + min_ss *= 2; + +#ifdef PTHREAD_STACK_MIN + if (min_ss < PTHREAD_STACK_MIN) + min_ss = PTHREAD_STACK_MIN; +#endif + + if (gcj::stack_size > 0 && gcj::stack_size < min_ss) + gcj::stack_size = min_ss; } -void -_Jv_ThreadInitData (_Jv_Thread_t **data, java::lang::Thread *obj) +_Jv_Thread_t * +_Jv_ThreadInitData (java::lang::Thread *obj) { - _Jv_Thread_t *info = new _Jv_Thread_t; - info->flags = 0; - info->thread_obj = obj; + _Jv_Thread_t *data = (_Jv_Thread_t *) _Jv_Malloc (sizeof (_Jv_Thread_t)); + data->flags = 0; + data->thread_obj = obj; - pthread_mutex_init (&info->wait_mutex, NULL); - pthread_cond_init (&info->wait_cond, 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; param.sched_priority = prio; - pthread_setschedparam (data->thread, SCHED_RR, ¶m); + pthread_setschedparam (data->thread, SCHED_OTHER, ¶m); } +#endif +} + +void +_Jv_ThreadRegister (_Jv_Thread_t *data) +{ + 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. + _Jv_BlockSigchld(); +} + +void +_Jv_ThreadUnRegister () +{ + pthread_setspecific (_Jv_ThreadKey, NULL); + pthread_setspecific (_Jv_ThreadDataKey, NULL); } // This function is called when a thread is started. We don't arrange @@ -333,16 +634,10 @@ really_start (void *x) { struct starter *info = (struct starter *) x; - pthread_setspecific (_Jv_ThreadKey, info->data->thread_obj); - pthread_setspecific (_Jv_ThreadDataKey, info->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. - info->data->thread = pthread_self (); + _Jv_ThreadRegister (info->data); info->method (info->data->thread_obj); - + if (! (info->data->flags & FLAG_DAEMON)) { pthread_mutex_lock (&daemon_mutex); @@ -351,12 +646,6 @@ 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; } @@ -373,12 +662,24 @@ _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. + _Jv_BlockSigchld(); + param.sched_priority = thread->getPriority(); pthread_attr_init (&attr); pthread_attr_setschedparam (&attr, ¶m); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + + // Set stack size if -Xss option was given. + if (gcj::stack_size > 0) + { + int e = pthread_attr_setstacksize (&attr, gcj::stack_size); + if (e != 0) + JvFail (strerror (e)); + } - // FIXME: handle marking the info object for GC. info = (struct starter *) _Jv_AllocBytes (sizeof (struct starter)); info->method = meth; info->data = data; @@ -398,7 +699,7 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, if (r) { const char* msg = "Cannot create additional threads"; - JvThrow (new java::lang::OutOfMemoryError (JvNewStringUTF (msg))); + throw new java::lang::OutOfMemoryError (JvNewStringUTF (msg)); } } @@ -410,3 +711,22 @@ _Jv_ThreadWait (void) pthread_cond_wait (&daemon_cond, &daemon_mutex); pthread_mutex_unlock (&daemon_mutex); } + +#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_t self = pthread_self(); + sce -> high_sp_bits = high_sp_bits; + write_barrier(); + sce -> self = self; + return self; +} + +#endif /* SLOW_PTHREAD_SELF */