X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fthreads%2Fposix%2Fthread-posix.cpp;h=a68cede39cf91c405bd86f2180fd3f137c519e65;hb=9d9877f7595070f5a7aedba5ae43a4b96a383a25;hp=8b789d6d34818af78ca6e0a59a55abc907e5fdf1;hpb=142b53c6e64878f8db3ef0981eaeb39e547fe6e7;p=cacao.git diff --git a/src/threads/posix/thread-posix.cpp b/src/threads/posix/thread-posix.cpp index 8b789d6d3..a68cede39 100644 --- a/src/threads/posix/thread-posix.cpp +++ b/src/threads/posix/thread-posix.cpp @@ -1,6 +1,6 @@ /* src/threads/posix/thread-posix.cpp - POSIX thread functions - Copyright (C) 1996-2005, 2006, 2007, 2008 + Copyright (C) 1996-2005, 2006, 2007, 2008, 2010 CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO This file is part of CACAO. @@ -27,6 +27,8 @@ /* XXX cleanup these includes */ +#define __STDC_LIMIT_MACROS + #include #include #include @@ -43,54 +45,50 @@ #include "arch.h" #include "mm/gc.hpp" -#include "mm/memory.h" +#include "mm/memory.hpp" #if defined(ENABLE_GC_CACAO) # include "mm/cacao-gc/gc.h" #endif -#include "native/jni.h" #include "native/llni.h" -#include "native/native.h" - -#include "native/include/java_lang_Object.h" -#include "native/include/java_lang_String.h" -#include "native/include/java_lang_Throwable.h" -#include "native/include/java_lang_Thread.h" - -#if defined(ENABLE_JAVASE) -# include "native/include/java_lang_ThreadGroup.h" -#endif - -#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) -# include "native/include/java_lang_VMThread.h" -#endif +#include "native/native.hpp" #include "threads/condition.hpp" -#include "threads/lock-common.h" +#include "threads/lock.hpp" #include "threads/mutex.hpp" -#include "threads/threadlist.h" +#include "threads/threadlist.hpp" #include "threads/thread.hpp" -#include "toolbox/logging.h" +#include "toolbox/logging.hpp" -#include "vm/builtin.h" +#include "vm/jit/builtin.hpp" #include "vm/exceptions.hpp" #include "vm/global.h" -#include "vm/signallocal.h" +#include "vm/globals.hpp" +#include "vm/hook.hpp" +#include "vm/javaobjects.hpp" +#include "vm/options.h" +#include "vm/os.hpp" +#include "vm/signallocal.hpp" #include "vm/string.hpp" #include "vm/vm.hpp" +#if defined(ENABLE_STATISTICS) +# include "vm/statistics.h" +#endif + #include "vm/jit/asmpart.h" -#include "vmcore/globals.hpp" -#include "vmcore/options.h" +#if defined(__DARWIN__) -#if defined(ENABLE_STATISTICS) -# include "vmcore/statistics.h" -#endif +typedef struct { + Mutex* mutex; + Condition* cond; + int value; +} sem_t; -#if !defined(__DARWIN__) +#else # include #endif @@ -100,6 +98,8 @@ # define GC_IRIX_THREADS #elif defined(__DARWIN__) # define GC_DARWIN_THREADS +#elif defined(__SOLARIS__) +# define GC_SOLARIS_THREADS #endif #if defined(ENABLE_GC_BOEHM) @@ -108,23 +108,16 @@ # include "mm/boehm-gc/include/gc.h" #endif -#if defined(ENABLE_JVMTI) -#include "native/jvmti/cacaodbg.h" -#endif - - -// FIXME For now we export everything as C functions. -extern "C" { #if defined(__DARWIN__) /* Darwin has no working semaphore implementation. This one is taken from Boehm-GC. */ /* - This is a very simple semaphore implementation for darwin. It + This is a very simple semaphore implementation for Darwin. It is implemented in terms of pthreads calls so it isn't async signal safe. This isn't a problem because signals aren't used to - suspend threads on darwin. + suspend threads on Darwin. */ static int sem_init(sem_t *sem, int pshared, int value) @@ -132,12 +125,9 @@ static int sem_init(sem_t *sem, int pshared, int value) if (pshared) assert(0); - sem->value = value; - sem->mutex = new Mutex(); - - if (pthread_cond_init(&sem->cond, NULL) < 0) - return -1; + sem->cond = new Condition(); + sem->value = value; return 0; } @@ -145,14 +135,8 @@ static int sem_init(sem_t *sem, int pshared, int value) static int sem_post(sem_t *sem) { sem->mutex->lock(); - sem->value++; - - if (pthread_cond_signal(&sem->cond) < 0) { - sem->mutex->unlock(); - return -1; - } - + sem->cond->signal(); sem->mutex->unlock(); return 0; @@ -163,12 +147,10 @@ static int sem_wait(sem_t *sem) sem->mutex->lock(); while (sem->value == 0) { -#error We cannot call pthread_cond_wait on a Mutex-class pointer. - pthread_cond_wait(&sem->cond, &sem->mutex); + sem->cond->wait(sem->mutex); } sem->value--; - sem->mutex->unlock(); return 0; @@ -176,9 +158,7 @@ static int sem_wait(sem_t *sem) static int sem_destroy(sem_t *sem) { - if (pthread_cond_destroy(&sem->cond) < 0) - return -1; - + delete sem->cond; delete sem->mutex; return 0; @@ -459,33 +439,6 @@ void threads_startworld(void) #endif -/* threads_impl_thread_init **************************************************** - - Initialize OS-level locking constructs in threadobject. - - IN: - t....the threadobject - -*******************************************************************************/ - -void threads_impl_thread_init(threadobject *t) -{ - /* initialize the mutex and the condition */ - - t->flc_lock = new Mutex(); - t->flc_cond = new Condition(); - - t->waitmutex = new Mutex(); - t->waitcond = new Condition(); - - t->suspendmutex = new Mutex(); - t->suspendcond = new Condition(); - -#if defined(ENABLE_TLH) - tlh_init(&(t->tlh)); -#endif -} - /* threads_impl_thread_clear *************************************************** Clears all fields in threadobject the way an MZERO would have @@ -536,7 +489,7 @@ void threads_impl_thread_clear(threadobject *t) t->es = NULL; #endif - MZERO(&t->dumpinfo, dumpinfo_t, 1); + // Simply reuse the existing dump memory. } /* threads_impl_thread_reuse *************************************************** @@ -602,21 +555,21 @@ void threads_impl_thread_free(threadobject *t) result = pthread_cond_destroy(&(t->flc_cond)); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); + os::abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); delete t->waitmutex; result = pthread_cond_destroy(&(t->waitcond)); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); + os::abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); delete t->suspendmutex; result = pthread_cond_destroy(&(t->suspendcond)); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); + os::abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); } #endif @@ -632,8 +585,6 @@ void threads_impl_thread_free(threadobject *t) void threads_impl_preinit(void) { - int result; - stopworldlock = new Mutex(); /* initialize exit mutex and condition (on exit we join all @@ -650,9 +601,9 @@ void threads_impl_preinit(void) #endif #if !defined(HAVE___THREAD) - result = pthread_key_create(&thread_current_key, NULL); + int result = pthread_key_create(&thread_current_key, NULL); if (result != 0) - vm_abort_errnum(result, "threads_impl_preinit: pthread_key_create failed"); + os::abort_errnum(result, "threads_impl_preinit: pthread_key_create failed"); #endif } @@ -726,12 +677,12 @@ void threads_impl_init(void) result = pthread_attr_init(&attr); if (result != 0) - vm_abort_errnum(result, "threads_impl_init: pthread_attr_init failed"); + os::abort_errnum(result, "threads_impl_init: pthread_attr_init failed"); result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (result != 0) - vm_abort_errnum(result, "threads_impl_init: pthread_attr_setdetachstate failed"); + os::abort_errnum(result, "threads_impl_init: pthread_attr_setdetachstate failed"); } @@ -756,17 +707,12 @@ void threads_impl_init(void) static void *threads_startup_thread(void *arg) { - startupinfo *startup; - threadobject *t; - java_lang_Thread *object; -#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) - java_lang_VMThread *vmt; -#endif - sem_t *psem; - classinfo *c; - methodinfo *m; - java_handle_t *o; - functionptr function; + startupinfo *startup; + threadobject *t; + sem_t *psem; + classinfo *c; + methodinfo *m; + functionptr function; #if defined(ENABLE_GC_BOEHM) # if !defined(__DARWIN__) @@ -829,13 +775,13 @@ static void *threads_startup_thread(void *arg) # endif #endif - /* get the java.lang.Thread object for this thread */ - - object = (java_lang_Thread *) thread_get_object(t); + // Get the java.lang.Thread object for this thread. + java_handle_t* object = thread_get_object(t); + java_lang_Thread jlt(object); /* set our priority */ - threads_set_thread_priority(t->tid, LLNI_field_direct(object, priority)); + threads_set_thread_priority(t->tid, jlt.get_priority()); /* Thread is completely initialized. */ @@ -854,12 +800,8 @@ static void *threads_startup_thread(void *arg) thread->_global_sp = (Cell *) (intrp_thread_stack + opt_stacksize); #endif -#if defined(ENABLE_JVMTI) - /* fire thread start event */ - - if (jvmti) - jvmti_ThreadStartEnd(JVMTI_EVENT_THREAD_START); -#endif + // Hook point just before the threads initial method is executed. + Hook::thread_start(t); DEBUGTHREADS("starting", t); @@ -883,45 +825,25 @@ static void *threads_startup_thread(void *arg) if (m == NULL) vm_abort("threads_startup_thread: run() method not found in class"); - /* set ThreadMXBean variables */ - -/* _Jv_jvm->java_lang_management_ThreadMXBean_ThreadCount++; */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_TotalStartedThreadCount++; */ - -/* if (_Jv_jvm->java_lang_management_ThreadMXBean_ThreadCount > */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_PeakThreadCount) */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_PeakThreadCount = */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_ThreadCount; */ -#warning Move to C++ - #if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) - /* we need to start the run method of java.lang.VMThread */ - LLNI_field_get_ref(object, vmThread, vmt); - o = (java_handle_t *) vmt; + // We need to start the run method of java.lang.VMThread. + java_lang_VMThread jlvmt(jlt.get_vmThread()); + java_handle_t* h = jlvmt.get_handle(); #elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) || defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1) - o = (java_handle_t *) object; + + java_handle_t* h = jlt.get_handle(); + #else # error unknown classpath configuration #endif /* Run the thread. */ - (void) vm_call_method(m, o); + (void) vm_call_method(m, h); } else { - /* set ThreadMXBean variables */ - -/* _Jv_jvm->java_lang_management_ThreadMXBean_ThreadCount++; */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_TotalStartedThreadCount++; */ - -/* if (_Jv_jvm->java_lang_management_ThreadMXBean_ThreadCount > */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_PeakThreadCount) */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_PeakThreadCount = */ -/* _Jv_jvm->java_lang_management_ThreadMXBean_ThreadCount; */ -#warning Move to C++ - /* call passed function, e.g. finalizer_thread */ (function)(); @@ -929,22 +851,13 @@ static void *threads_startup_thread(void *arg) DEBUGTHREADS("stopping", t); -#if defined(ENABLE_JVMTI) - /* fire thread end event */ - - if (jvmti) - jvmti_ThreadStartEnd(JVMTI_EVENT_THREAD_END); -#endif + // Hook point just after the threads initial method returned. + Hook::thread_end(t); /* We ignore the return value. */ (void) thread_detach_current_thread(); - /* set ThreadMXBean variables */ - -/* _Jv_jvm->java_lang_management_ThreadMXBean_ThreadCount--; */ -#warning Move to C++ - return NULL; } @@ -985,33 +898,33 @@ void threads_impl_thread_start(threadobject *thread, functionptr f) result = pthread_attr_init(&attr); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_start: pthread_attr_init failed"); + os::abort_errnum(result, "threads_impl_thread_start: pthread_attr_init failed"); result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_start: pthread_attr_setdetachstate failed"); + os::abort_errnum(result, "threads_impl_thread_start: pthread_attr_setdetachstate failed"); /* initialize thread stacksize */ result = pthread_attr_setstacksize(&attr, opt_stacksize); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_start: pthread_attr_setstacksize failed"); + os::abort_errnum(result, "threads_impl_thread_start: pthread_attr_setstacksize failed"); /* create the thread */ result = pthread_create(&(thread->tid), &attr, threads_startup_thread, &startup); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_start: pthread_create failed"); + os::abort_errnum(result, "threads_impl_thread_start: pthread_create failed"); /* destroy the thread attributes */ result = pthread_attr_destroy(&attr); if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_start: pthread_attr_destroy failed"); + os::abort_errnum(result, "threads_impl_thread_start: pthread_attr_destroy failed"); /* signal that pthread_create has returned, so thread->tid is valid */ @@ -1056,19 +969,7 @@ void threads_set_thread_priority(pthread_t tid, int priority) */ bool thread_detach_current_thread(void) { - threadobject *t; - bool result; - java_lang_Thread *object; - java_handle_t *o; -#if defined(ENABLE_JAVASE) - java_lang_ThreadGroup *group; - java_handle_t *e; - void *handler; - classinfo *c; - methodinfo *m; -#endif - - t = thread_get_current(); + threadobject* t = thread_get_current(); /* Sanity check. */ @@ -1077,23 +978,22 @@ bool thread_detach_current_thread(void) /* If the given thread has already been detached, this operation is a no-op. */ - result = thread_is_attached(t); - - if (result == false) + if (thread_is_attached(t) == false) return true; DEBUGTHREADS("detaching", t); - object = (java_lang_Thread *) thread_get_object(t); + java_handle_t* object = thread_get_object(t); + java_lang_Thread jlt(object); #if defined(ENABLE_JAVASE) - LLNI_field_get_ref(object, group, group); + java_handle_t* group = jlt.get_group(); /* If there's an uncaught exception, call uncaughtException on the thread's exception handler, or the thread's group if this is unset. */ - e = exceptions_get_and_clear_exception(); + java_handle_t* e = exceptions_get_and_clear_exception(); if (e != NULL) { /* We use the type void* for handler here, as it's not trivial @@ -1101,30 +1001,37 @@ bool thread_detach_current_thread(void) header file with cacaoh. */ # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) - LLNI_field_get_ref(object, exceptionHandler, handler); + + java_handle_t* handler = jlt.get_exceptionHandler(); + # elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) - LLNI_field_get_ref(object, uncaughtExceptionHandler, handler); + + java_handle_t* handler = jlt.get_uncaughtExceptionHandler(); + # endif + classinfo* c; + java_handle_t* h; + if (handler != NULL) { LLNI_class_get(handler, c); - o = (java_handle_t *) handler; + h = (java_handle_t *) handler; } else { LLNI_class_get(group, c); - o = (java_handle_t *) group; + h = (java_handle_t *) group; } - m = class_resolveclassmethod(c, - utf_uncaughtException, - utf_java_lang_Thread_java_lang_Throwable__V, - NULL, - true); + methodinfo* m = class_resolveclassmethod(c, + utf_uncaughtException, + utf_java_lang_Thread_java_lang_Throwable__V, + NULL, + true); if (m == NULL) return false; - (void) vm_call_method(m, o, object, e); + (void) vm_call_method(m, h, object, e); if (exceptions_get_exception()) return false; @@ -1135,20 +1042,21 @@ bool thread_detach_current_thread(void) /* Remove thread from the thread group. */ if (group != NULL) { + classinfo* c; LLNI_class_get(group, c); # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) - m = class_resolveclassmethod(c, - utf_removeThread, - utf_java_lang_Thread__V, - class_java_lang_ThreadGroup, - true); + methodinfo* m = class_resolveclassmethod(c, + utf_removeThread, + utf_java_lang_Thread__V, + class_java_lang_ThreadGroup, + true); # elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) - m = class_resolveclassmethod(c, - utf_remove, - utf_java_lang_Thread__V, - class_java_lang_ThreadGroup, - true); + methodinfo* m = class_resolveclassmethod(c, + utf_remove, + utf_java_lang_Thread__V, + class_java_lang_ThreadGroup, + true); # else # error unknown classpath configuration # endif @@ -1156,17 +1064,14 @@ bool thread_detach_current_thread(void) if (m == NULL) return false; - o = (java_handle_t *) group; - - (void) vm_call_method(m, o, object); + (void) vm_call_method(m, group, object); if (exceptions_get_exception()) return false; - /* Reset the threadgroup in the Java thread object (Mauve - test: gnu/testlet/java/lang/Thread/getThreadGroup). */ - - LLNI_field_set_ref(object, group, NULL); + // Clear the ThreadGroup in the Java thread object (Mauve + // test: gnu/testlet/java/lang/Thread/getThreadGroup). + jlt.set_group(NULL); } #endif @@ -1177,15 +1082,13 @@ bool thread_detach_current_thread(void) /* Notify all threads waiting on this thread. These are joining this thread. */ - o = (java_handle_t *) object; - /* XXX Care about exceptions? */ - (void) lock_monitor_enter(o); + (void) lock_monitor_enter(jlt.get_handle()); - lock_notify_all_object(o); + lock_notify_all_object(jlt.get_handle()); /* XXX Care about exceptions? */ - (void) lock_monitor_exit(o); + (void) lock_monitor_exit(jlt.get_handle()); /* Enter the join-mutex before calling thread_free, so threads_join_all_threads gets the correct number of non-daemon @@ -1206,134 +1109,146 @@ bool thread_detach_current_thread(void) } -/* threads_suspend_thread ****************************************************** - - Suspend the passed thread. Execution stops until the thread - is explicitly resumend again. - - IN: - reason.....Reason for suspending this thread. - -*******************************************************************************/ - -bool threads_suspend_thread(threadobject *thread, s4 reason) +/** + * Internal helper function which suspends the current thread. This is + * the core method of the suspension mechanism actually blocking the + * execution until the suspension reason is cleared again. Note that + * the current thread needs to hold the suspension mutex while calling + * this function. + */ +static void threads_suspend_self() { - /* acquire the suspendmutex */ - thread->suspendmutex->lock(); - - if (thread->suspended) { - thread->suspendmutex->unlock(); - return false; - } + threadobject* thread = THREADOBJECT; - /* set the reason for the suspension */ - thread->suspend_reason = reason; + DEBUGTHREADS("suspending", thread); - /* send the suspend signal to the thread */ - assert(thread != THREADOBJECT); - if (pthread_kill(thread->tid, SIGUSR1) != 0) - vm_abort("threads_suspend_thread: pthread_kill failed: %s", - strerror(errno)); + // Mark thread as suspended. + assert(!thread->suspended); + assert(thread->suspend_reason != SUSPEND_REASON_NONE); + thread->suspended = true; - /* REMEMBER: do not release the suspendmutex, this is done - by the thread itself in threads_suspend_ack(). */ + // Acknowledge the suspension. + thread->suspendcond->broadcast(); - return true; -} +#if defined(ENABLE_GC_CACAO) + // If we are stopping the world, we should send a global ack. + if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD) + threads_sem_post(&suspend_ack); +#endif + // Release the suspension mutex and wait till we are resumed. + thread->suspendcond->wait(thread->suspendmutex); -/* threads_suspend_ack ********************************************************* +#if defined(ENABLE_GC_CACAO) + // XXX This is propably not ok! + // If we are starting the world, we should send a global ack. + if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD) + threads_sem_post(&suspend_ack); +#endif - Acknowledges the suspension of the current thread. + // Mark thread as not suspended. + assert(thread->suspended); + assert(thread->suspend_reason == SUSPEND_REASON_NONE); + thread->suspended = false; - IN: - pc.....The PC where the thread suspended its execution. - sp.....The SP before the thread suspended its execution. + DEBUGTHREADS("resuming", thread); +} -*******************************************************************************/ -#if defined(ENABLE_GC_CACAO) -void threads_suspend_ack(u1* pc, u1* sp) +/** + * Suspend the passed thread. Execution of that thread stops until the thread + * is explicitly resumend again. + * + * @param thread The thread to be suspended. + * @param reason Reason for suspending the given thread. + * @return True of operation was successful, false otherwise. + */ +bool threads_suspend_thread(threadobject *thread, int32_t reason) { - threadobject *thread; - - thread = THREADOBJECT; + // Sanity check. + assert(reason != SUSPEND_REASON_NONE); - assert(thread->suspend_reason != 0); + // Guard this with the suspension mutex. + MutexLocker ml(*thread->suspendmutex); - /* TODO: remember dump memory size */ - - /* inform the GC about the suspension */ - if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD && gc_pending) { + // Check if thread is already suspended. + if (thread->suspended) + return false; - /* check if the GC wants to leave the thread running */ - if (!gc_suspend(thread, pc, sp)) { + // Check if thread is in the process of suspending. + if (thread->suspend_reason != SUSPEND_REASON_NONE) + return false; - /* REMEMBER: we do not unlock the suspendmutex because the thread - will suspend itself again at a later time */ - return; + // Set the reason for suspending the thread. + thread->suspend_reason = reason; - } + if (thread == THREADOBJECT) { + // We already hold the suspension mutex and can suspend ourselves + // immediately without using signals at all. + threads_suspend_self(); } - - /* mark this thread as suspended and remember the PC */ - thread->pc = pc; - thread->suspended = true; - - /* if we are stopping the world, we should send a global ack */ - if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD) { - threads_sem_post(&suspend_ack); + else { + // Send the suspend signal to the other thread. + if (pthread_kill(thread->tid, SIGUSR1) != 0) + os::abort_errno("threads_suspend_thread: pthread_kill failed"); + + // Wait for the thread to acknowledge the suspension. + // XXX A possible optimization would be to not wait here, but you + // better think this through twice before trying it! + thread->suspendcond->wait(thread->suspendmutex); } - DEBUGTHREADS("suspending", thread); + return true; +} - /* release the suspension mutex and wait till we are resumed */ - thread->suspendcond->wait(thread->suspendmutex); - DEBUGTHREADS("resuming", thread); +/** + * Resumes execution of the passed thread. + * + * @param thread The thread to be resumed. + * @param reason Reason for suspending the given thread. + * @return True of operation was successful, false otherwise. + */ +bool threads_resume_thread(threadobject *thread, int32_t reason) +{ + // Sanity check. + assert(thread != THREADOBJECT); + assert(reason != SUSPEND_REASON_NONE); - /* if we are stopping the world, we should send a global ack */ - if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD) { - threads_sem_post(&suspend_ack); - } + // Guard this with the suspension mutex. + MutexLocker ml(*thread->suspendmutex); - /* TODO: free dump memory */ + // Check if thread really is suspended. + if (!thread->suspended) + return false; - /* release the suspendmutex */ - thread->suspendmutex->unlock(); -} -#endif + // Threads can only be resumed for the same reason they were suspended. + if (thread->suspend_reason != reason) + return false; + // Clear the reason for suspending the thread. + thread->suspend_reason = SUSPEND_REASON_NONE; -/* threads_resume_thread ******************************************************* + // Tell everyone that the thread should resume. + thread->suspendcond->broadcast(); - Resumes the execution of the passed thread. + return true; +} -*******************************************************************************/ -#if defined(ENABLE_GC_CACAO) -bool threads_resume_thread(threadobject *thread) +/** + * Acknowledges the suspension of the current thread. + */ +void threads_suspend_ack() { - /* acquire the suspendmutex */ - thread->suspendmutex->lock(); - - if (!thread->suspended) { - thread->suspendmutex->unlock(); - return false; - } - - thread->suspended = false; - - /* tell everyone that the thread should resume */ - assert(thread != THREADOBJECT); - thread->suspendcond->broadcast(); + threadobject* thread = THREADOBJECT; - /* release the suspendmutex */ - thread->suspendmutex->unlock(); + // Guard this with the suspension mutex. + MutexLocker ml(*thread->suspendmutex); - return true; + // Suspend ourselves while holding the suspension mutex. + threads_suspend_self(); } -#endif /* threads_join_all_threads **************************************************** @@ -1362,7 +1277,7 @@ void threads_join_all_threads(void) compare against 1 because the current (main thread) is also a non-daemon thread. */ - while (threadlist_get_non_daemons() > 1) + while (ThreadList::get_number_of_non_daemon_threads() > 1) cond_join->wait(mutex_join); /* leave join mutex */ @@ -1439,7 +1354,7 @@ static bool threads_current_time_is_earlier_than(const struct timespec *tv) *******************************************************************************/ -static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTime) +static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTime, bool parking) { // Acquire the waitmutex. t->waitmutex->lock(); @@ -1448,10 +1363,13 @@ static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTi if (wakeupTime->tv_sec || wakeupTime->tv_nsec) { /* with timeout */ - while (!t->interrupted && !t->signaled + while (!t->interrupted && !(parking ? t->park_permit : t->signaled) && threads_current_time_is_earlier_than(wakeupTime)) { - thread_set_state_timed_waiting(t); + if (parking) + thread_set_state_timed_parked(t); + else + thread_set_state_timed_waiting(t); t->waitcond->timedwait(t->waitmutex, wakeupTime); @@ -1460,8 +1378,11 @@ static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTi } else { /* no timeout */ - while (!t->interrupted && !t->signaled) { - thread_set_state_waiting(t); + while (!t->interrupted && !(parking ? t->park_permit : t->signaled)) { + if (parking) + thread_set_state_parked(t); + else + thread_set_state_waiting(t); t->waitcond->wait(t->waitmutex); @@ -1469,6 +1390,9 @@ static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTi } } + if (parking) + t->park_permit = false; + // Release the waitmutex. t->waitmutex->unlock(); } @@ -1497,7 +1421,7 @@ void threads_wait_with_timeout_relative(threadobject *thread, s8 millis, /* wait */ - threads_wait_with_timeout(thread, &wakeupTime); + threads_wait_with_timeout(thread, &wakeupTime, false); } @@ -1516,20 +1440,26 @@ void threads_wait_with_timeout_relative(threadobject *thread, s8 millis, static void threads_calc_absolute_time(struct timespec *tm, s8 millis, s4 nanos) { - if ((millis != 0x7fffffffffffffffLLU) && (millis || nanos)) { + // (at least with GNU classpath) we know that 0 <= nanos <= 999999 + do { + if (!millis && !nanos) + break; struct timeval tv; - long nsec; gettimeofday(&tv, NULL); - tv.tv_sec += millis / 1000; + s8 secs = tv.tv_sec + millis / 1000; + if (secs > INT32_MAX) // integer overflow + break; + tv.tv_sec = secs; millis %= 1000; - nsec = tv.tv_usec * 1000 + (s4) millis * 1000000 + nanos; + long nsec = tv.tv_usec * 1000 + (s4) millis * 1000000 + nanos; tm->tv_sec = tv.tv_sec + nsec / 1000000000; + if (tm->tv_sec < tv.tv_sec) // integer overflow + break; tm->tv_nsec = nsec % 1000000000; - } - else { - tm->tv_sec = 0; - tm->tv_nsec = 0; - } + return; + } while (0); + tm->tv_sec = 0; + tm->tv_nsec = 0; } @@ -1556,7 +1486,7 @@ void threads_thread_interrupt(threadobject *t) /* Interrupt blocking system call using a signal. */ - pthread_kill(t->tid, Signal_THREAD_INTERRUPT); + pthread_kill(t->tid, Signal_INTERRUPT_SYSTEM_CALL); t->waitcond->signal(); @@ -1608,7 +1538,7 @@ void threads_sleep(int64_t millis, int32_t nanos) else { threads_calc_absolute_time(&wakeupTime, millis, nanos); - threads_wait_with_timeout(t, &wakeupTime); + threads_wait_with_timeout(t, &wakeupTime, false); interrupted = thread_is_interrupted(t); @@ -1623,6 +1553,47 @@ void threads_sleep(int64_t millis, int32_t nanos) } } +/** + * Park the current thread for the specified amount of time or until a + * specified deadline. + * + * @param absolute Is the time in nanos a deadline or a duration? + * @param nanos Nanoseconds to park (absolute=false) + * or deadline in milliseconds (absolute=true) + */ +void threads_park(bool absolute, int64_t nanos) +{ + threadobject *t; + struct timespec wakeupTime; + + t = thread_get_current(); + + if (absolute) { + wakeupTime.tv_nsec = 0; + wakeupTime.tv_sec = nanos / 1000; /* milliseconds */ + } + else + threads_calc_absolute_time(&wakeupTime, nanos / 1000000, nanos % 1000000); + + threads_wait_with_timeout(t, &wakeupTime, true); +} + +/** + * Unpark the specified thread. + * + * @param t The thread to unpark. + */ +void threads_unpark(threadobject *t) +{ + t->waitmutex->lock(); + + t->waitcond->signal(); + + t->park_permit = true; + + t->waitmutex->unlock(); +} + /* threads_yield *************************************************************** @@ -1647,8 +1618,6 @@ void threads_tlh_remove_frame() { #endif -} // extern "C" - /* * These are local overrides for various environment variables in Emacs.