From: Christian Thalinger Date: Tue, 22 Jul 2008 08:33:15 +0000 (+0200) Subject: * src/threads/posix/thread-posix.c: Moved to .cpp. X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=9315a9e40325367f96af78974e9c21dc42d34618;p=cacao.git * src/threads/posix/thread-posix.c: Moved to .cpp. * src/threads/posix/thread-posix.h: Likewise. * src/threads/posix/thread-posix.cpp: New file. * src/threads/posix/thread-posix.hpp: Likewise. * src/threads/posix/Makefile.am (libthreadsposix_la_SOURCES): Changed filenames. * src/threads/thread.h: Changed include. --HG-- rename : src/threads/posix/thread-posix.c => src/threads/posix/thread-posix.cpp rename : src/threads/posix/thread-posix.h => src/threads/posix/thread-posix.hpp --- diff --git a/src/threads/posix/Makefile.am b/src/threads/posix/Makefile.am index bb18964f0..c4a9f7361 100644 --- a/src/threads/posix/Makefile.am +++ b/src/threads/posix/Makefile.am @@ -33,8 +33,8 @@ libthreadsposix_la_SOURCES = \ lock.c \ lock.h \ mutex-posix.hpp \ - thread-posix.c \ - thread-posix.h + thread-posix.cpp \ + thread-posix.hpp ## Local variables: diff --git a/src/threads/posix/thread-posix.c b/src/threads/posix/thread-posix.c deleted file mode 100644 index 9682eba01..000000000 --- a/src/threads/posix/thread-posix.c +++ /dev/null @@ -1,1657 +0,0 @@ -/* src/threads/posix/thread-posix.c - POSIX thread functions - - Copyright (C) 1996-2005, 2006, 2007, 2008 - CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO - - This file is part of CACAO. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - -*/ - - -#include "config.h" - -/* XXX cleanup these includes */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "vm/types.h" - -#include "arch.h" - -#include "mm/gc.hpp" -#include "mm/memory.h" - -#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 "threads/condition.hpp" -#include "threads/lock-common.h" -#include "threads/mutex.hpp" -#include "threads/threadlist.h" -#include "threads/thread.h" - -#include "toolbox/logging.h" - -#include "vm/builtin.h" -#include "vm/exceptions.h" -#include "vm/global.h" -#include "vm/stringlocal.h" -#include "vm/vm.hpp" - -#include "vm/jit/asmpart.h" - -#include "vmcore/globals.hpp" -#include "vmcore/options.h" - -#if defined(ENABLE_STATISTICS) -# include "vmcore/statistics.h" -#endif - -#if !defined(__DARWIN__) -# include -#endif - -#if defined(__LINUX__) -# define GC_LINUX_THREADS -#elif defined(__IRIX__) -# define GC_IRIX_THREADS -#elif defined(__DARWIN__) -# define GC_DARWIN_THREADS -#endif - -#if defined(ENABLE_GC_BOEHM) -/* We need to include Boehm's gc.h here because it overrides - pthread_create and friends. */ -# include "mm/boehm-gc/include/gc.h" -#endif - -#if defined(ENABLE_JVMTI) -#include "native/jvmti/cacaodbg.h" -#endif - -#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 - 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. -*/ - -static int sem_init(sem_t *sem, int pshared, int value) -{ - if (pshared) - assert(0); - - sem->value = value; - - sem->mutex = Mutex_new(); - - if (pthread_cond_init(&sem->cond, NULL) < 0) - return -1; - - return 0; -} - -static int sem_post(sem_t *sem) -{ - Mutex_lock(sem->mutex); - - sem->value++; - - if (pthread_cond_signal(&sem->cond) < 0) { - Mutex_unlock(sem->mutex); - return -1; - } - - Mutex_unlock(sem->mutex); - - return 0; -} - -static int sem_wait(sem_t *sem) -{ - Mutex_lock(sem->mutex); - - while (sem->value == 0) { -#error We cannot call pthread_cond_wait on a Mutex-class pointer. - pthread_cond_wait(&sem->cond, &sem->mutex); - } - - sem->value--; - - Mutex_unlock(sem->mutex); - - return 0; -} - -static int sem_destroy(sem_t *sem) -{ - if (pthread_cond_destroy(&sem->cond) < 0) - return -1; - - Mutex_destroy(sem->mutex); - - return 0; -} -#endif /* defined(__DARWIN__) */ - - -/* startupinfo ***************************************************************** - - Struct used to pass info from threads_start_thread to - threads_startup_thread. - -******************************************************************************/ - -typedef struct { - threadobject *thread; /* threadobject for this thread */ - functionptr function; /* function to run in the new thread */ - sem_t *psem; /* signals when thread has been entered */ - /* in the thread list */ - sem_t *psem_first; /* signals when pthread_create has returned */ -} startupinfo; - - -/* prototypes *****************************************************************/ - -static void threads_calc_absolute_time(struct timespec *tm, s8 millis, s4 nanos); - - -/******************************************************************************/ -/* GLOBAL VARIABLES */ -/******************************************************************************/ - -/* the thread object of the current thread */ -/* This is either a thread-local variable defined with __thread, or */ -/* a thread-specific value stored with key threads_current_threadobject_key. */ -#if defined(HAVE___THREAD) -__thread threadobject *thread_current; -#else -pthread_key_t thread_current_key; -#endif - -/* global mutex for stop-the-world */ -static Mutex* stopworldlock; - -#if defined(ENABLE_GC_CACAO) -/* global mutex for the GC */ -static Mutex* mutex_gc; -#endif - -/* global mutex and condition for joining threads on exit */ -static Mutex* mutex_join; -static Condition* cond_join; - -#if defined(ENABLE_GC_CACAO) -/* semaphore used for acknowleding thread suspension */ -static sem_t suspend_ack; -#endif - - -/* threads_sem_init ************************************************************ - - Initialize a semaphore. Checks against errors and interruptions. - - IN: - sem..............the semaphore to initialize - shared...........true if this semaphore will be shared between processes - value............the initial value for the semaphore - -*******************************************************************************/ - -void threads_sem_init(sem_t *sem, bool shared, int value) -{ - int r; - - assert(sem); - - do { - r = sem_init(sem, shared, value); - if (r == 0) - return; - } while (errno == EINTR); - - vm_abort("sem_init failed: %s", strerror(errno)); -} - - -/* threads_sem_wait ************************************************************ - - Wait for a semaphore, non-interruptible. - - IMPORTANT: Always use this function instead of `sem_wait` directly, as - `sem_wait` may be interrupted by signals! - - IN: - sem..............the semaphore to wait on - -*******************************************************************************/ - -void threads_sem_wait(sem_t *sem) -{ - int r; - - assert(sem); - - do { - r = sem_wait(sem); - if (r == 0) - return; - } while (errno == EINTR); - - vm_abort("sem_wait failed: %s", strerror(errno)); -} - - -/* threads_sem_post ************************************************************ - - Increase the count of a semaphore. Checks for errors. - - IN: - sem..............the semaphore to increase the count of - -*******************************************************************************/ - -void threads_sem_post(sem_t *sem) -{ - int r; - - assert(sem); - - /* unlike sem_wait, sem_post is not interruptible */ - - r = sem_post(sem); - if (r == 0) - return; - - vm_abort("sem_post failed: %s", strerror(errno)); -} - - -/* threads_stopworld *********************************************************** - - Stops the world from turning. All threads except the calling one - are suspended. The function returns as soon as all threads have - acknowledged their suspension. - -*******************************************************************************/ - -#if defined(ENABLE_GC_CACAO) -void threads_stopworld(void) -{ -#if !defined(__DARWIN__) && !defined(__CYGWIN__) - threadobject *t; - threadobject *self; - bool result; - s4 count, i; -#endif - - Mutex_lock(stopworldlock); - - /* lock the threads lists */ - - threadlist_lock(); - -#if defined(__DARWIN__) - /*threads_cast_darwinstop();*/ - assert(0); -#elif defined(__CYGWIN__) - /* TODO */ - assert(0); -#else - self = THREADOBJECT; - - DEBUGTHREADS("stops World", self); - - count = 0; - - /* suspend all running threads */ - for (t = threadlist_first(); t != NULL; t = threadlist_next(t)) { - /* don't send the signal to ourself */ - - if (t == self) - continue; - - /* don't send the signal to NEW threads (because they are not - completely initialized) */ - - if (t->state == THREAD_STATE_NEW) - continue; - - /* send the signal */ - - result = threads_suspend_thread(t, SUSPEND_REASON_STOPWORLD); - assert(result); - - /* increase threads count */ - - count++; - } - - /* wait for all threads signaled to suspend */ - for (i = 0; i < count; i++) - threads_sem_wait(&suspend_ack); -#endif - - /* ATTENTION: Don't unlock the threads-lists here so that - non-signaled NEW threads can't change their state and execute - code. */ -} -#endif - - -/* threads_startworld ********************************************************** - - Starts the world again after it has previously been stopped. - -*******************************************************************************/ - -#if defined(ENABLE_GC_CACAO) -void threads_startworld(void) -{ -#if !defined(__DARWIN__) && !defined(__CYGWIN__) - threadobject *t; - threadobject *self; - bool result; - s4 count, i; -#endif - -#if defined(__DARWIN__) - /*threads_cast_darwinresume();*/ - assert(0); -#elif defined(__IRIX__) - threads_cast_irixresume(); -#elif defined(__CYGWIN__) - /* TODO */ - assert(0); -#else - self = THREADOBJECT; - - DEBUGTHREADS("starts World", self); - - count = 0; - - /* resume all thread we haltet */ - for (t = threadlist_first(); t != NULL; t = threadlist_next(t)) { - /* don't send the signal to ourself */ - - if (t == self) - continue; - - /* don't send the signal to NEW threads (because they are not - completely initialized) */ - - if (t->state == THREAD_STATE_NEW) - continue; - - /* send the signal */ - - result = threads_resume_thread(t); - assert(result); - - /* increase threads count */ - - count++; - } - - /* wait for all threads signaled to suspend */ - for (i = 0; i < count; i++) - threads_sem_wait(&suspend_ack); - -#endif - - /* unlock the threads lists */ - - threadlist_unlock(); - - Mutex_unlock(stopworldlock); -} -#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 = Mutex_new(); - t->flc_cond = Condition_new(); - - t->waitmutex = Mutex_new(); - t->waitcond = Condition_new(); - - t->suspendmutex = Mutex_new(); - t->suspendcond = Condition_new(); - -#if defined(ENABLE_TLH) - tlh_init(&(t->tlh)); -#endif -} - -/* threads_impl_thread_clear *************************************************** - - Clears all fields in threadobject the way an MZERO would have - done. MZERO cannot be used anymore because it would mess up the - pthread_* bits. - - IN: - t....the threadobject - -*******************************************************************************/ - -void threads_impl_thread_clear(threadobject *t) -{ - t->object = NULL; - - t->thinlock = 0; - - t->index = 0; - t->flags = 0; - t->state = 0; - - t->tid = 0; - -#if defined(__DARWIN__) - t->mach_thread = 0; -#endif - - t->interrupted = false; - t->signaled = false; - - t->suspended = false; - t->suspend_reason = 0; - - t->pc = NULL; - - t->_exceptionptr = NULL; - t->_stackframeinfo = NULL; - t->_localref_table = NULL; - -#if defined(ENABLE_INTRP) - t->_global_sp = NULL; -#endif - -#if defined(ENABLE_GC_CACAO) - t->gc_critical = false; - - t->ss = NULL; - t->es = NULL; -#endif - - MZERO(&t->dumpinfo, dumpinfo_t, 1); -} - -/* threads_impl_thread_reuse *************************************************** - - Resets some implementation fields in threadobject. This was - previously done in threads_impl_thread_new. - - IN: - t....the threadobject - -*******************************************************************************/ - -void threads_impl_thread_reuse(threadobject *t) -{ - /* get the pthread id */ - - t->tid = pthread_self(); - -#if defined(ENABLE_DEBUG_FILTER) - /* Initialize filter counters */ - t->filterverbosecallctr[0] = 0; - t->filterverbosecallctr[1] = 0; -#endif - -#if !defined(NDEBUG) - t->tracejavacallindent = 0; - t->tracejavacallcount = 0; -#endif - - t->flc_bit = false; - t->flc_next = NULL; - t->flc_list = NULL; - -/* not really needed */ - t->flc_object = NULL; - -#if defined(ENABLE_TLH) - tlh_destroy(&(t->tlh)); - tlh_init(&(t->tlh)); -#endif -} - - -/* threads_impl_thread_free **************************************************** - - Cleanup thread stuff. - - IN: - t....the threadobject - -*******************************************************************************/ - -#if 0 -/* never used */ -void threads_impl_thread_free(threadobject *t) -{ - int result; - - /* Destroy the mutex and the condition. */ - - Mutex_delete(t->flc_lock); - - result = pthread_cond_destroy(&(t->flc_cond)); - - if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); - - Mutex_delete(t->waitmutex); - - result = pthread_cond_destroy(&(t->waitcond)); - - if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); - - Mutex_delete(t->suspendmutex); - - result = pthread_cond_destroy(&(t->suspendcond)); - - if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); -} -#endif - - -/* threads_impl_preinit ******************************************************** - - Do some early initialization of stuff required. - - ATTENTION: Do NOT use any Java heap allocation here, as gc_init() - is called AFTER this function! - -*******************************************************************************/ - -void threads_impl_preinit(void) -{ - int result; - - stopworldlock = Mutex_new(); - - /* initialize exit mutex and condition (on exit we join all - threads) */ - - mutex_join = Mutex_new(); - cond_join = Condition_new(); - -#if defined(ENABLE_GC_CACAO) - /* initialize the GC mutex & suspend semaphore */ - - mutex_gc = Mutex_new(); - threads_sem_init(&suspend_ack, 0, 0); -#endif - -#if !defined(HAVE___THREAD) - result = pthread_key_create(&thread_current_key, NULL); - if (result != 0) - vm_abort_errnum(result, "threads_impl_preinit: pthread_key_create failed"); -#endif -} - - -/* threads_mutex_gc_lock ******************************************************* - - Enter the global GC mutex. - -*******************************************************************************/ - -#if defined(ENABLE_GC_CACAO) -void threads_mutex_gc_lock(void) -{ - Mutex_lock(mutex_gc); -} -#endif - - -/* threads_mutex_gc_unlock ***************************************************** - - Leave the global GC mutex. - -*******************************************************************************/ - -#if defined(ENABLE_GC_CACAO) -void threads_mutex_gc_unlock(void) -{ - Mutex_unlock(mutex_gc); -} -#endif - -/* threads_mutex_join_lock ***************************************************** - - Enter the join mutex. - -*******************************************************************************/ - -void threads_mutex_join_lock(void) -{ - Mutex_lock(mutex_join); -} - - -/* threads_mutex_join_unlock *************************************************** - - Leave the join mutex. - -*******************************************************************************/ - -void threads_mutex_join_unlock(void) -{ - Mutex_unlock(mutex_join); -} - - -/* threads_impl_init *********************************************************** - - Initializes the implementation specific bits. - -*******************************************************************************/ - -void threads_impl_init(void) -{ - pthread_attr_t attr; - int result; - - threads_set_thread_priority(pthread_self(), NORM_PRIORITY); - - /* Initialize the thread attribute object. */ - - result = pthread_attr_init(&attr); - - if (result != 0) - vm_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"); -} - - -/* threads_startup_thread ****************************************************** - - Thread startup function called by pthread_create. - - Thread which have a startup.function != NULL are marked as internal - threads. All other threads are threated as normal Java threads. - - NOTE: This function is not called directly by pthread_create. The Boehm GC - inserts its own GC_start_routine in between, which then calls - threads_startup. - - IN: - arg..........the argument passed to pthread_create, ie. a pointer to - a startupinfo struct. CAUTION: When the `psem` semaphore - is posted, the startupinfo struct becomes invalid! (It - is allocated on the stack of threads_start_thread.) - -******************************************************************************/ - -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; - -#if defined(ENABLE_GC_BOEHM) -# if !defined(__DARWIN__) - struct GC_stack_base sb; - int result; -# endif -#endif - -#if defined(ENABLE_INTRP) - u1 *intrp_thread_stack; -#endif - -#if defined(ENABLE_INTRP) - /* create interpreter stack */ - - if (opt_intrp) { - intrp_thread_stack = GCMNEW(u1, opt_stacksize); - MSET(intrp_thread_stack, 0, u1, opt_stacksize); - } - else - intrp_thread_stack = NULL; -#endif - - /* get passed startupinfo structure and the values in there */ - - startup = arg; - - t = startup->thread; - function = startup->function; - psem = startup->psem; - - /* Seems like we've encountered a situation where thread->tid was - not set by pthread_create. We alleviate this problem by waiting - for pthread_create to return. */ - - threads_sem_wait(startup->psem_first); - -#if defined(__DARWIN__) - t->mach_thread = mach_thread_self(); -#endif - - /* Now that we are in the new thread, we can store the internal - thread data-structure in the TSD. */ - - thread_set_current(t); - -#if defined(ENABLE_GC_BOEHM) -# if defined(__DARWIN__) - // This is currently not implemented in Boehm-GC. Just fail silently. -# else - /* Register the thread with Boehm-GC. This must happen before the - thread allocates any memory from the GC heap.*/ - - result = GC_get_stack_base(&sb); - - if (result != 0) - vm_abort("threads_startup_thread: GC_get_stack_base failed: result=%d", result); - - GC_register_my_thread(&sb); -# endif -#endif - - /* get the java.lang.Thread object for this thread */ - - object = (java_lang_Thread *) thread_get_object(t); - - /* set our priority */ - - threads_set_thread_priority(t->tid, LLNI_field_direct(object, priority)); - - /* Thread is completely initialized. */ - - thread_set_state_runnable(t); - - /* tell threads_startup_thread that we registered ourselves */ - /* CAUTION: *startup becomes invalid with this! */ - - startup = NULL; - threads_sem_post(psem); - -#if defined(ENABLE_INTRP) - /* set interpreter stack */ - - if (opt_intrp) - 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 - - DEBUGTHREADS("starting", t); - - /* find and run the Thread.run()V method if no other function was passed */ - - if (function == NULL) { -#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) - /* We need to start the run method of - java.lang.VMThread. Since this is a final class, we can use - the class object directly. */ - - c = class_java_lang_VMThread; -#elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) || defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1) - LLNI_class_get(object, c); -#else -# error unknown classpath configuration -#endif - - m = class_resolveclassmethod(c, utf_run, utf_void__void, c, true); - - 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; - -#elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) || defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1) - o = (java_handle_t *) object; -#else -# error unknown classpath configuration -#endif - - /* Run the thread. */ - - (void) vm_call_method(m, o); - } - 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)(); - } - - DEBUGTHREADS("stopping", t); - -#if defined(ENABLE_JVMTI) - /* fire thread end event */ - - if (jvmti) - jvmti_ThreadStartEnd(JVMTI_EVENT_THREAD_END); -#endif - - /* 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; -} - - -/* threads_impl_thread_start *************************************************** - - Start a thread in the JVM. Both (vm internal and java) thread - objects exist. - - IN: - thread....the thread object - f.........function to run in the new thread. NULL means that the - "run" method of the object `t` should be called - -******************************************************************************/ - -void threads_impl_thread_start(threadobject *thread, functionptr f) -{ - sem_t sem; - sem_t sem_first; - pthread_attr_t attr; - startupinfo startup; - int result; - - /* fill startupinfo structure passed by pthread_create to - * threads_startup_thread */ - - startup.thread = thread; - startup.function = f; /* maybe we don't call Thread.run()V */ - startup.psem = &sem; - startup.psem_first = &sem_first; - - threads_sem_init(&sem, 0, 0); - threads_sem_init(&sem_first, 0, 0); - - /* Initialize thread attributes. */ - - result = pthread_attr_init(&attr); - - if (result != 0) - vm_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"); - - /* 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"); - - /* 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"); - - /* destroy the thread attributes */ - - result = pthread_attr_destroy(&attr); - - if (result != 0) - vm_abort_errnum(result, "threads_impl_thread_start: pthread_attr_destroy failed"); - - /* signal that pthread_create has returned, so thread->tid is valid */ - - threads_sem_post(&sem_first); - - /* wait here until the thread has entered itself into the thread list */ - - threads_sem_wait(&sem); - - /* cleanup */ - - sem_destroy(&sem); - sem_destroy(&sem_first); -} - - -/* threads_set_thread_priority ************************************************* - - Set the priority of the given thread. - - IN: - tid..........thread id - priority.....priority to set - -******************************************************************************/ - -void threads_set_thread_priority(pthread_t tid, int priority) -{ - struct sched_param schedp; - int policy; - - pthread_getschedparam(tid, &policy, &schedp); - schedp.sched_priority = priority; - pthread_setschedparam(tid, policy, &schedp); -} - - -/** - * Detaches the current thread from the VM. - * - * @return true on success, false otherwise - */ -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(); - - /* Sanity check. */ - - assert(t != NULL); - - /* If the given thread has already been detached, this operation - is a no-op. */ - - result = thread_is_attached(t); - - if (result == false) - return true; - - DEBUGTHREADS("detaching", t); - - object = (java_lang_Thread *) thread_get_object(t); - -#if defined(ENABLE_JAVASE) - LLNI_field_get_ref(object, group, 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(); - - if (e != NULL) { - /* We use the type void* for handler here, as it's not trivial - to build the java_lang_Thread_UncaughtExceptionHandler - header file with cacaoh. */ - -# if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) - LLNI_field_get_ref(object, exceptionHandler, handler); -# elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) - LLNI_field_get_ref(object, uncaughtExceptionHandler, handler); -# endif - - if (handler != NULL) { - LLNI_class_get(handler, c); - o = (java_handle_t *) handler; - } - else { - LLNI_class_get(group, c); - o = (java_handle_t *) group; - } - - 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); - - if (exceptions_get_exception()) - return false; - } - - /* XXX TWISTI: should all threads be in a ThreadGroup? */ - - /* Remove thread from the thread group. */ - - if (group != NULL) { - 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); -# elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) - m = class_resolveclassmethod(c, - utf_remove, - utf_java_lang_Thread__V, - class_java_lang_ThreadGroup, - true); -# else -# error unknown classpath configuration -# endif - - if (m == NULL) - return false; - - o = (java_handle_t *) group; - - (void) vm_call_method(m, o, 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); - } -#endif - - /* Thread has terminated. */ - - thread_set_state_terminated(t); - - /* 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); - - lock_notify_all_object(o); - - /* XXX Care about exceptions? */ - (void) lock_monitor_exit(o); - - /* Enter the join-mutex before calling thread_free, so - threads_join_all_threads gets the correct number of non-daemon - threads. */ - - threads_mutex_join_lock(); - - /* Free the internal thread data-structure. */ - - thread_free(t); - - /* Signal that this thread has finished and leave the mutex. */ - - Condition_signal(cond_join); - threads_mutex_join_unlock(); - - return true; -} - - -/* 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) -{ - /* acquire the suspendmutex */ - Mutex_lock(thread->suspendmutex); - - if (thread->suspended) { - Mutex_unlock(thread->suspendmutex); - return false; - } - - /* set the reason for the suspension */ - thread->suspend_reason = reason; - - /* 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)); - - /* REMEMBER: do not release the suspendmutex, this is done - by the thread itself in threads_suspend_ack(). */ - - return true; -} - - -/* threads_suspend_ack ********************************************************* - - Acknowledges the suspension of the current thread. - - IN: - pc.....The PC where the thread suspended its execution. - sp.....The SP before the thread suspended its execution. - -*******************************************************************************/ - -#if defined(ENABLE_GC_CACAO) -void threads_suspend_ack(u1* pc, u1* sp) -{ - threadobject *thread; - - thread = THREADOBJECT; - - assert(thread->suspend_reason != 0); - - /* TODO: remember dump memory size */ - - /* inform the GC about the suspension */ - if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD && gc_pending) { - - /* check if the GC wants to leave the thread running */ - if (!gc_suspend(thread, pc, sp)) { - - /* REMEMBER: we do not unlock the suspendmutex because the thread - will suspend itself again at a later time */ - return; - - } - } - - /* 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); - } - - DEBUGTHREADS("suspending", thread); - - /* release the suspension mutex and wait till we are resumed */ - Condition_wait(thread->suspendcond, thread->suspendmutex); - - DEBUGTHREADS("resuming", thread); - - /* if we are stopping the world, we should send a global ack */ - if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD) { - threads_sem_post(&suspend_ack); - } - - /* TODO: free dump memory */ - - /* release the suspendmutex */ - Mutex_unlock(thread->suspendmutex); -} -#endif - - -/* threads_resume_thread ******************************************************* - - Resumes the execution of the passed thread. - -*******************************************************************************/ - -#if defined(ENABLE_GC_CACAO) -bool threads_resume_thread(threadobject *thread) -{ - /* acquire the suspendmutex */ - Mutex_lock(thread->suspendmutex); - - if (!thread->suspended) { - Mutex_unlock(thread->suspendmutex); - return false; - } - - thread->suspended = false; - - /* tell everyone that the thread should resume */ - assert(thread != THREADOBJECT); - Condition_broadcast(thread->suspendcond); - - /* release the suspendmutex */ - Mutex_unlock(thread->suspendmutex); - - return true; -} -#endif - - -/* threads_join_all_threads **************************************************** - - Join all non-daemon threads. - -*******************************************************************************/ - -void threads_join_all_threads(void) -{ - threadobject *t; - - /* get current thread */ - - t = THREADOBJECT; - - /* This thread is waiting for all non-daemon threads to exit. */ - - thread_set_state_waiting(t); - - /* enter join mutex */ - - threads_mutex_join_lock(); - - /* Wait for condition as long as we have non-daemon threads. We - compare against 1 because the current (main thread) is also a - non-daemon thread. */ - - while (threadlist_get_non_daemons() > 1) - Condition_wait(cond_join, mutex_join); - - /* leave join mutex */ - - threads_mutex_join_unlock(); -} - - -/* threads_timespec_earlier **************************************************** - - Return true if timespec tv1 is earlier than timespec tv2. - - IN: - tv1..........first timespec - tv2..........second timespec - - RETURN VALUE: - true, if the first timespec is earlier - -*******************************************************************************/ - -static inline bool threads_timespec_earlier(const struct timespec *tv1, - const struct timespec *tv2) -{ - return (tv1->tv_sec < tv2->tv_sec) - || - (tv1->tv_sec == tv2->tv_sec && tv1->tv_nsec < tv2->tv_nsec); -} - - -/* threads_current_time_is_earlier_than **************************************** - - Check if the current time is earlier than the given timespec. - - IN: - tv...........the timespec to compare against - - RETURN VALUE: - true, if the current time is earlier - -*******************************************************************************/ - -static bool threads_current_time_is_earlier_than(const struct timespec *tv) -{ - struct timeval tvnow; - struct timespec tsnow; - - /* get current time */ - - if (gettimeofday(&tvnow, NULL) != 0) - vm_abort("gettimeofday failed: %s\n", strerror(errno)); - - /* convert it to a timespec */ - - tsnow.tv_sec = tvnow.tv_sec; - tsnow.tv_nsec = tvnow.tv_usec * 1000; - - /* compare current time with the given timespec */ - - return threads_timespec_earlier(&tsnow, tv); -} - - -/* threads_wait_with_timeout *************************************************** - - Wait until the given point in time on a monitor until either - we are notified, we are interrupted, or the time is up. - - IN: - t............the current thread - wakeupTime...absolute (latest) wakeup time - If both tv_sec and tv_nsec are zero, this function - waits for an unlimited amount of time. - -*******************************************************************************/ - -static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTime) -{ - /* acquire the waitmutex */ - - Mutex_lock(t->waitmutex); - - /* wait on waitcond */ - - if (wakeupTime->tv_sec || wakeupTime->tv_nsec) { - /* with timeout */ - while (!t->interrupted && !t->signaled - && threads_current_time_is_earlier_than(wakeupTime)) - { - thread_set_state_timed_waiting(t); - - Condition_timedwait(t->waitcond, t->waitmutex, wakeupTime); - - thread_set_state_runnable(t); - } - } - else { - /* no timeout */ - while (!t->interrupted && !t->signaled) { - thread_set_state_waiting(t); - - Condition_wait(t->waitcond, t->waitmutex); - - thread_set_state_runnable(t); - } - } - - /* release the waitmutex */ - - Mutex_unlock(t->waitmutex); -} - - -/* threads_wait_with_timeout_relative ****************************************** - - Wait for the given maximum amount of time on a monitor until either - we are notified, we are interrupted, or the time is up. - - IN: - t............the current thread - millis.......milliseconds to wait - nanos........nanoseconds to wait - -*******************************************************************************/ - -void threads_wait_with_timeout_relative(threadobject *thread, s8 millis, - s4 nanos) -{ - struct timespec wakeupTime; - - /* calculate the the (latest) wakeup time */ - - threads_calc_absolute_time(&wakeupTime, millis, nanos); - - /* wait */ - - threads_wait_with_timeout(thread, &wakeupTime); -} - - -/* threads_calc_absolute_time ************************************************** - - Calculate the absolute point in time a given number of ms and ns from now. - - IN: - millis............milliseconds from now - nanos.............nanoseconds from now - - OUT: - *tm...............receives the timespec of the absolute point in time - -*******************************************************************************/ - -static void threads_calc_absolute_time(struct timespec *tm, s8 millis, s4 nanos) -{ - if ((millis != 0x7fffffffffffffffLLU) && (millis || nanos)) { - struct timeval tv; - long nsec; - gettimeofday(&tv, NULL); - tv.tv_sec += millis / 1000; - millis %= 1000; - nsec = tv.tv_usec * 1000 + (s4) millis * 1000000 + nanos; - tm->tv_sec = tv.tv_sec + nsec / 1000000000; - tm->tv_nsec = nsec % 1000000000; - } - else { - tm->tv_sec = 0; - tm->tv_nsec = 0; - } -} - - -/* threads_thread_interrupt **************************************************** - - Interrupt the given thread. - - The thread gets the "waitcond" signal and - its interrupted flag is set to true. - - IN: - thread............the thread to interrupt - -*******************************************************************************/ - -void threads_thread_interrupt(threadobject *thread) -{ -#if defined(__LINUX__) && defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) - /* See openjdk/jdk/src/solaris/native/java/net/linux_close.c, "sigWakeup" */ - int sig = (__SIGRTMAX - 2); -#else - int sig = SIGHUP; -#endif - /* Signal the thread a "waitcond" and tell it that it has been - interrupted. */ - - Mutex_lock(thread->waitmutex); - - DEBUGTHREADS("interrupted", thread); - - /* Interrupt blocking system call using a signal. */ - - pthread_kill(thread->tid, sig); - - Condition_signal(thread->waitcond); - - thread->interrupted = true; - - Mutex_unlock(thread->waitmutex); -} - - -/* threads_sleep *************************************************************** - - Sleep the current thread for the specified amount of time. - -*******************************************************************************/ - -void threads_sleep(int64_t millis, int32_t nanos) -{ - threadobject *t; - struct timespec wakeupTime; - bool interrupted; - - if (millis < 0) { -/* exceptions_throw_illegalargumentexception("timeout value is negative"); */ - exceptions_throw_illegalargumentexception(); - return; - } - - t = thread_get_current(); - - if (thread_is_interrupted(t) && !exceptions_get_exception()) { - /* Clear interrupted flag (Mauve test: - gnu/testlet/java/lang/Thread/interrupt). */ - - thread_set_interrupted(t, false); - -/* exceptions_throw_interruptedexception("sleep interrupted"); */ - exceptions_throw_interruptedexception(); - return; - } - - threads_calc_absolute_time(&wakeupTime, millis, nanos); - - threads_wait_with_timeout(t, &wakeupTime); - - interrupted = thread_is_interrupted(t); - - if (interrupted) { - thread_set_interrupted(t, false); - - /* An other exception could have been thrown - (e.g. ThreadDeathException). */ - - if (!exceptions_get_exception()) - exceptions_throw_interruptedexception(); - } -} - - -/* threads_yield *************************************************************** - - Yield to the scheduler. - -*******************************************************************************/ - -void threads_yield(void) -{ - sched_yield(); -} - -#if defined(ENABLE_TLH) - -void threads_tlh_add_frame() { - tlh_add_frame(&(THREADOBJECT->tlh)); -} - -void threads_tlh_remove_frame() { - tlh_remove_frame(&(THREADOBJECT->tlh)); -} - -#endif - -/* - * These are local overrides for various environment variables in Emacs. - * Please do not remove this and leave it at the end of the file, where - * Emacs will automagically detect them. - * --------------------------------------------------------------------- - * Local variables: - * mode: c - * indent-tabs-mode: t - * c-basic-offset: 4 - * tab-width: 4 - * End: - * vim:noexpandtab:sw=4:ts=4: - */ diff --git a/src/threads/posix/thread-posix.cpp b/src/threads/posix/thread-posix.cpp new file mode 100644 index 000000000..f5f45d170 --- /dev/null +++ b/src/threads/posix/thread-posix.cpp @@ -0,0 +1,1662 @@ +/* src/threads/posix/thread-posix.cpp - POSIX thread functions + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#include "config.h" + +/* XXX cleanup these includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "vm/types.h" + +#include "arch.h" + +#include "mm/gc.hpp" +#include "mm/memory.h" + +#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 "threads/condition.hpp" +#include "threads/lock-common.h" +#include "threads/mutex.hpp" +#include "threads/threadlist.h" +#include "threads/thread.h" + +#include "toolbox/logging.h" + +#include "vm/builtin.h" +#include "vm/exceptions.h" +#include "vm/global.h" +#include "vm/stringlocal.h" +#include "vm/vm.hpp" + +#include "vm/jit/asmpart.h" + +#include "vmcore/globals.hpp" +#include "vmcore/options.h" + +#if defined(ENABLE_STATISTICS) +# include "vmcore/statistics.h" +#endif + +#if !defined(__DARWIN__) +# include +#endif + +#if defined(__LINUX__) +# define GC_LINUX_THREADS +#elif defined(__IRIX__) +# define GC_IRIX_THREADS +#elif defined(__DARWIN__) +# define GC_DARWIN_THREADS +#endif + +#if defined(ENABLE_GC_BOEHM) +/* We need to include Boehm's gc.h here because it overrides + pthread_create and friends. */ +# 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 + 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. +*/ + +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; + + return 0; +} + +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->mutex->unlock(); + + return 0; +} + +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->value--; + + sem->mutex->unlock(); + + return 0; +} + +static int sem_destroy(sem_t *sem) +{ + if (pthread_cond_destroy(&sem->cond) < 0) + return -1; + + delete sem->mutex; + + return 0; +} +#endif /* defined(__DARWIN__) */ + + +/* startupinfo ***************************************************************** + + Struct used to pass info from threads_start_thread to + threads_startup_thread. + +******************************************************************************/ + +typedef struct { + threadobject *thread; /* threadobject for this thread */ + functionptr function; /* function to run in the new thread */ + sem_t *psem; /* signals when thread has been entered */ + /* in the thread list */ + sem_t *psem_first; /* signals when pthread_create has returned */ +} startupinfo; + + +/* prototypes *****************************************************************/ + +static void threads_calc_absolute_time(struct timespec *tm, s8 millis, s4 nanos); + + +/******************************************************************************/ +/* GLOBAL VARIABLES */ +/******************************************************************************/ + +/* the thread object of the current thread */ +/* This is either a thread-local variable defined with __thread, or */ +/* a thread-specific value stored with key threads_current_threadobject_key. */ +#if defined(HAVE___THREAD) +__thread threadobject *thread_current; +#else +pthread_key_t thread_current_key; +#endif + +/* global mutex for stop-the-world */ +static Mutex* stopworldlock; + +#if defined(ENABLE_GC_CACAO) +/* global mutex for the GC */ +static Mutex* mutex_gc; +#endif + +/* global mutex and condition for joining threads on exit */ +static Mutex* mutex_join; +static Condition* cond_join; + +#if defined(ENABLE_GC_CACAO) +/* semaphore used for acknowleding thread suspension */ +static sem_t suspend_ack; +#endif + + +/* threads_sem_init ************************************************************ + + Initialize a semaphore. Checks against errors and interruptions. + + IN: + sem..............the semaphore to initialize + shared...........true if this semaphore will be shared between processes + value............the initial value for the semaphore + +*******************************************************************************/ + +void threads_sem_init(sem_t *sem, bool shared, int value) +{ + int r; + + assert(sem); + + do { + r = sem_init(sem, shared, value); + if (r == 0) + return; + } while (errno == EINTR); + + vm_abort("sem_init failed: %s", strerror(errno)); +} + + +/* threads_sem_wait ************************************************************ + + Wait for a semaphore, non-interruptible. + + IMPORTANT: Always use this function instead of `sem_wait` directly, as + `sem_wait` may be interrupted by signals! + + IN: + sem..............the semaphore to wait on + +*******************************************************************************/ + +void threads_sem_wait(sem_t *sem) +{ + int r; + + assert(sem); + + do { + r = sem_wait(sem); + if (r == 0) + return; + } while (errno == EINTR); + + vm_abort("sem_wait failed: %s", strerror(errno)); +} + + +/* threads_sem_post ************************************************************ + + Increase the count of a semaphore. Checks for errors. + + IN: + sem..............the semaphore to increase the count of + +*******************************************************************************/ + +void threads_sem_post(sem_t *sem) +{ + int r; + + assert(sem); + + /* unlike sem_wait, sem_post is not interruptible */ + + r = sem_post(sem); + if (r == 0) + return; + + vm_abort("sem_post failed: %s", strerror(errno)); +} + + +/* threads_stopworld *********************************************************** + + Stops the world from turning. All threads except the calling one + are suspended. The function returns as soon as all threads have + acknowledged their suspension. + +*******************************************************************************/ + +#if defined(ENABLE_GC_CACAO) +void threads_stopworld(void) +{ +#if !defined(__DARWIN__) && !defined(__CYGWIN__) + threadobject *t; + threadobject *self; + bool result; + s4 count, i; +#endif + + stopworldlock->lock(); + + /* lock the threads lists */ + + threadlist_lock(); + +#if defined(__DARWIN__) + /*threads_cast_darwinstop();*/ + assert(0); +#elif defined(__CYGWIN__) + /* TODO */ + assert(0); +#else + self = THREADOBJECT; + + DEBUGTHREADS("stops World", self); + + count = 0; + + /* suspend all running threads */ + for (t = threadlist_first(); t != NULL; t = threadlist_next(t)) { + /* don't send the signal to ourself */ + + if (t == self) + continue; + + /* don't send the signal to NEW threads (because they are not + completely initialized) */ + + if (t->state == THREAD_STATE_NEW) + continue; + + /* send the signal */ + + result = threads_suspend_thread(t, SUSPEND_REASON_STOPWORLD); + assert(result); + + /* increase threads count */ + + count++; + } + + /* wait for all threads signaled to suspend */ + for (i = 0; i < count; i++) + threads_sem_wait(&suspend_ack); +#endif + + /* ATTENTION: Don't unlock the threads-lists here so that + non-signaled NEW threads can't change their state and execute + code. */ +} +#endif + + +/* threads_startworld ********************************************************** + + Starts the world again after it has previously been stopped. + +*******************************************************************************/ + +#if defined(ENABLE_GC_CACAO) +void threads_startworld(void) +{ +#if !defined(__DARWIN__) && !defined(__CYGWIN__) + threadobject *t; + threadobject *self; + bool result; + s4 count, i; +#endif + +#if defined(__DARWIN__) + /*threads_cast_darwinresume();*/ + assert(0); +#elif defined(__IRIX__) + threads_cast_irixresume(); +#elif defined(__CYGWIN__) + /* TODO */ + assert(0); +#else + self = THREADOBJECT; + + DEBUGTHREADS("starts World", self); + + count = 0; + + /* resume all thread we haltet */ + for (t = threadlist_first(); t != NULL; t = threadlist_next(t)) { + /* don't send the signal to ourself */ + + if (t == self) + continue; + + /* don't send the signal to NEW threads (because they are not + completely initialized) */ + + if (t->state == THREAD_STATE_NEW) + continue; + + /* send the signal */ + + result = threads_resume_thread(t); + assert(result); + + /* increase threads count */ + + count++; + } + + /* wait for all threads signaled to suspend */ + for (i = 0; i < count; i++) + threads_sem_wait(&suspend_ack); + +#endif + + /* unlock the threads lists */ + + threadlist_unlock(); + + stopworldlock->unlock(); +} +#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 + done. MZERO cannot be used anymore because it would mess up the + pthread_* bits. + + IN: + t....the threadobject + +*******************************************************************************/ + +void threads_impl_thread_clear(threadobject *t) +{ + t->object = NULL; + + t->thinlock = 0; + + t->index = 0; + t->flags = 0; + t->state = 0; + + t->tid = 0; + +#if defined(__DARWIN__) + t->mach_thread = 0; +#endif + + t->interrupted = false; + t->signaled = false; + + t->suspended = false; + t->suspend_reason = 0; + + t->pc = NULL; + + t->_exceptionptr = NULL; + t->_stackframeinfo = NULL; + t->_localref_table = NULL; + +#if defined(ENABLE_INTRP) + t->_global_sp = NULL; +#endif + +#if defined(ENABLE_GC_CACAO) + t->gc_critical = false; + + t->ss = NULL; + t->es = NULL; +#endif + + MZERO(&t->dumpinfo, dumpinfo_t, 1); +} + +/* threads_impl_thread_reuse *************************************************** + + Resets some implementation fields in threadobject. This was + previously done in threads_impl_thread_new. + + IN: + t....the threadobject + +*******************************************************************************/ + +void threads_impl_thread_reuse(threadobject *t) +{ + /* get the pthread id */ + + t->tid = pthread_self(); + +#if defined(ENABLE_DEBUG_FILTER) + /* Initialize filter counters */ + t->filterverbosecallctr[0] = 0; + t->filterverbosecallctr[1] = 0; +#endif + +#if !defined(NDEBUG) + t->tracejavacallindent = 0; + t->tracejavacallcount = 0; +#endif + + t->flc_bit = false; + t->flc_next = NULL; + t->flc_list = NULL; + +/* not really needed */ + t->flc_object = NULL; + +#if defined(ENABLE_TLH) + tlh_destroy(&(t->tlh)); + tlh_init(&(t->tlh)); +#endif +} + + +/* threads_impl_thread_free **************************************************** + + Cleanup thread stuff. + + IN: + t....the threadobject + +*******************************************************************************/ + +#if 0 +/* never used */ +void threads_impl_thread_free(threadobject *t) +{ + int result; + + /* Destroy the mutex and the condition. */ + + delete t->flc_lock; + + result = pthread_cond_destroy(&(t->flc_cond)); + + if (result != 0) + vm_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"); + + delete t->suspendmutex; + + result = pthread_cond_destroy(&(t->suspendcond)); + + if (result != 0) + vm_abort_errnum(result, "threads_impl_thread_free: pthread_cond_destroy failed"); +} +#endif + + +/* threads_impl_preinit ******************************************************** + + Do some early initialization of stuff required. + + ATTENTION: Do NOT use any Java heap allocation here, as gc_init() + is called AFTER this function! + +*******************************************************************************/ + +void threads_impl_preinit(void) +{ + int result; + + stopworldlock = new Mutex(); + + /* initialize exit mutex and condition (on exit we join all + threads) */ + + mutex_join = new Mutex(); + cond_join = new Condition(); + +#if defined(ENABLE_GC_CACAO) + /* initialize the GC mutex & suspend semaphore */ + + mutex_gc = new Mutex(); + threads_sem_init(&suspend_ack, 0, 0); +#endif + +#if !defined(HAVE___THREAD) + result = pthread_key_create(&thread_current_key, NULL); + if (result != 0) + vm_abort_errnum(result, "threads_impl_preinit: pthread_key_create failed"); +#endif +} + + +/* threads_mutex_gc_lock ******************************************************* + + Enter the global GC mutex. + +*******************************************************************************/ + +#if defined(ENABLE_GC_CACAO) +void threads_mutex_gc_lock(void) +{ + mutex_gc->lock(); +} +#endif + + +/* threads_mutex_gc_unlock ***************************************************** + + Leave the global GC mutex. + +*******************************************************************************/ + +#if defined(ENABLE_GC_CACAO) +void threads_mutex_gc_unlock(void) +{ + mutex_gc->unlock(); +} +#endif + +/* threads_mutex_join_lock ***************************************************** + + Enter the join mutex. + +*******************************************************************************/ + +void threads_mutex_join_lock(void) +{ + mutex_join->lock(); +} + + +/* threads_mutex_join_unlock *************************************************** + + Leave the join mutex. + +*******************************************************************************/ + +void threads_mutex_join_unlock(void) +{ + mutex_join->unlock(); +} + + +/* threads_impl_init *********************************************************** + + Initializes the implementation specific bits. + +*******************************************************************************/ + +void threads_impl_init(void) +{ + pthread_attr_t attr; + int result; + + threads_set_thread_priority(pthread_self(), NORM_PRIORITY); + + /* Initialize the thread attribute object. */ + + result = pthread_attr_init(&attr); + + if (result != 0) + vm_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"); +} + + +/* threads_startup_thread ****************************************************** + + Thread startup function called by pthread_create. + + Thread which have a startup.function != NULL are marked as internal + threads. All other threads are threated as normal Java threads. + + NOTE: This function is not called directly by pthread_create. The Boehm GC + inserts its own GC_start_routine in between, which then calls + threads_startup. + + IN: + arg..........the argument passed to pthread_create, ie. a pointer to + a startupinfo struct. CAUTION: When the `psem` semaphore + is posted, the startupinfo struct becomes invalid! (It + is allocated on the stack of threads_start_thread.) + +******************************************************************************/ + +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; + +#if defined(ENABLE_GC_BOEHM) +# if !defined(__DARWIN__) + struct GC_stack_base sb; + int result; +# endif +#endif + +#if defined(ENABLE_INTRP) + u1 *intrp_thread_stack; +#endif + +#if defined(ENABLE_INTRP) + /* create interpreter stack */ + + if (opt_intrp) { + intrp_thread_stack = GCMNEW(u1, opt_stacksize); + MSET(intrp_thread_stack, 0, u1, opt_stacksize); + } + else + intrp_thread_stack = NULL; +#endif + + /* get passed startupinfo structure and the values in there */ + + startup = (startupinfo*) arg; + + t = startup->thread; + function = startup->function; + psem = startup->psem; + + /* Seems like we've encountered a situation where thread->tid was + not set by pthread_create. We alleviate this problem by waiting + for pthread_create to return. */ + + threads_sem_wait(startup->psem_first); + +#if defined(__DARWIN__) + t->mach_thread = mach_thread_self(); +#endif + + /* Now that we are in the new thread, we can store the internal + thread data-structure in the TSD. */ + + thread_set_current(t); + +#if defined(ENABLE_GC_BOEHM) +# if defined(__DARWIN__) + // This is currently not implemented in Boehm-GC. Just fail silently. +# else + /* Register the thread with Boehm-GC. This must happen before the + thread allocates any memory from the GC heap.*/ + + result = GC_get_stack_base(&sb); + + if (result != 0) + vm_abort("threads_startup_thread: GC_get_stack_base failed: result=%d", result); + + GC_register_my_thread(&sb); +# endif +#endif + + /* get the java.lang.Thread object for this thread */ + + object = (java_lang_Thread *) thread_get_object(t); + + /* set our priority */ + + threads_set_thread_priority(t->tid, LLNI_field_direct(object, priority)); + + /* Thread is completely initialized. */ + + thread_set_state_runnable(t); + + /* tell threads_startup_thread that we registered ourselves */ + /* CAUTION: *startup becomes invalid with this! */ + + startup = NULL; + threads_sem_post(psem); + +#if defined(ENABLE_INTRP) + /* set interpreter stack */ + + if (opt_intrp) + 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 + + DEBUGTHREADS("starting", t); + + /* find and run the Thread.run()V method if no other function was passed */ + + if (function == NULL) { +#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) + /* We need to start the run method of + java.lang.VMThread. Since this is a final class, we can use + the class object directly. */ + + c = class_java_lang_VMThread; +#elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) || defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1) + LLNI_class_get(object, c); +#else +# error unknown classpath configuration +#endif + + m = class_resolveclassmethod(c, utf_run, utf_void__void, c, true); + + 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; + +#elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) || defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1) + o = (java_handle_t *) object; +#else +# error unknown classpath configuration +#endif + + /* Run the thread. */ + + (void) vm_call_method(m, o); + } + 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)(); + } + + DEBUGTHREADS("stopping", t); + +#if defined(ENABLE_JVMTI) + /* fire thread end event */ + + if (jvmti) + jvmti_ThreadStartEnd(JVMTI_EVENT_THREAD_END); +#endif + + /* 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; +} + + +/* threads_impl_thread_start *************************************************** + + Start a thread in the JVM. Both (vm internal and java) thread + objects exist. + + IN: + thread....the thread object + f.........function to run in the new thread. NULL means that the + "run" method of the object `t` should be called + +******************************************************************************/ + +void threads_impl_thread_start(threadobject *thread, functionptr f) +{ + sem_t sem; + sem_t sem_first; + pthread_attr_t attr; + startupinfo startup; + int result; + + /* fill startupinfo structure passed by pthread_create to + * threads_startup_thread */ + + startup.thread = thread; + startup.function = f; /* maybe we don't call Thread.run()V */ + startup.psem = &sem; + startup.psem_first = &sem_first; + + threads_sem_init(&sem, 0, 0); + threads_sem_init(&sem_first, 0, 0); + + /* Initialize thread attributes. */ + + result = pthread_attr_init(&attr); + + if (result != 0) + vm_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"); + + /* 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"); + + /* 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"); + + /* destroy the thread attributes */ + + result = pthread_attr_destroy(&attr); + + if (result != 0) + vm_abort_errnum(result, "threads_impl_thread_start: pthread_attr_destroy failed"); + + /* signal that pthread_create has returned, so thread->tid is valid */ + + threads_sem_post(&sem_first); + + /* wait here until the thread has entered itself into the thread list */ + + threads_sem_wait(&sem); + + /* cleanup */ + + sem_destroy(&sem); + sem_destroy(&sem_first); +} + + +/* threads_set_thread_priority ************************************************* + + Set the priority of the given thread. + + IN: + tid..........thread id + priority.....priority to set + +******************************************************************************/ + +void threads_set_thread_priority(pthread_t tid, int priority) +{ + struct sched_param schedp; + int policy; + + pthread_getschedparam(tid, &policy, &schedp); + schedp.sched_priority = priority; + pthread_setschedparam(tid, policy, &schedp); +} + + +/** + * Detaches the current thread from the VM. + * + * @return true on success, false otherwise + */ +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(); + + /* Sanity check. */ + + assert(t != NULL); + + /* If the given thread has already been detached, this operation + is a no-op. */ + + result = thread_is_attached(t); + + if (result == false) + return true; + + DEBUGTHREADS("detaching", t); + + object = (java_lang_Thread *) thread_get_object(t); + +#if defined(ENABLE_JAVASE) + LLNI_field_get_ref(object, group, 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(); + + if (e != NULL) { + /* We use the type void* for handler here, as it's not trivial + to build the java_lang_Thread_UncaughtExceptionHandler + header file with cacaoh. */ + +# if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) + LLNI_field_get_ref(object, exceptionHandler, handler); +# elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) + LLNI_field_get_ref(object, uncaughtExceptionHandler, handler); +# endif + + if (handler != NULL) { + LLNI_class_get(handler, c); + o = (java_handle_t *) handler; + } + else { + LLNI_class_get(group, c); + o = (java_handle_t *) group; + } + + 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); + + if (exceptions_get_exception()) + return false; + } + + /* XXX TWISTI: should all threads be in a ThreadGroup? */ + + /* Remove thread from the thread group. */ + + if (group != NULL) { + 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); +# elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) + m = class_resolveclassmethod(c, + utf_remove, + utf_java_lang_Thread__V, + class_java_lang_ThreadGroup, + true); +# else +# error unknown classpath configuration +# endif + + if (m == NULL) + return false; + + o = (java_handle_t *) group; + + (void) vm_call_method(m, o, 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); + } +#endif + + /* Thread has terminated. */ + + thread_set_state_terminated(t); + + /* 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); + + lock_notify_all_object(o); + + /* XXX Care about exceptions? */ + (void) lock_monitor_exit(o); + + /* Enter the join-mutex before calling thread_free, so + threads_join_all_threads gets the correct number of non-daemon + threads. */ + + threads_mutex_join_lock(); + + /* Free the internal thread data-structure. */ + + thread_free(t); + + /* Signal that this thread has finished and leave the mutex. */ + + cond_join->signal(); + threads_mutex_join_unlock(); + + return true; +} + + +/* 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) +{ + /* acquire the suspendmutex */ + thread->suspendmutex->lock(); + + if (thread->suspended) { + thread->suspendmutex->unlock(); + return false; + } + + /* set the reason for the suspension */ + thread->suspend_reason = reason; + + /* 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)); + + /* REMEMBER: do not release the suspendmutex, this is done + by the thread itself in threads_suspend_ack(). */ + + return true; +} + + +/* threads_suspend_ack ********************************************************* + + Acknowledges the suspension of the current thread. + + IN: + pc.....The PC where the thread suspended its execution. + sp.....The SP before the thread suspended its execution. + +*******************************************************************************/ + +#if defined(ENABLE_GC_CACAO) +void threads_suspend_ack(u1* pc, u1* sp) +{ + threadobject *thread; + + thread = THREADOBJECT; + + assert(thread->suspend_reason != 0); + + /* TODO: remember dump memory size */ + + /* inform the GC about the suspension */ + if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD && gc_pending) { + + /* check if the GC wants to leave the thread running */ + if (!gc_suspend(thread, pc, sp)) { + + /* REMEMBER: we do not unlock the suspendmutex because the thread + will suspend itself again at a later time */ + return; + + } + } + + /* 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); + } + + DEBUGTHREADS("suspending", thread); + + /* release the suspension mutex and wait till we are resumed */ + thread->suspendcond->wait(thread->suspendmutex); + + DEBUGTHREADS("resuming", thread); + + /* if we are stopping the world, we should send a global ack */ + if (thread->suspend_reason == SUSPEND_REASON_STOPWORLD) { + threads_sem_post(&suspend_ack); + } + + /* TODO: free dump memory */ + + /* release the suspendmutex */ + thread->suspendmutex->unlock(); +} +#endif + + +/* threads_resume_thread ******************************************************* + + Resumes the execution of the passed thread. + +*******************************************************************************/ + +#if defined(ENABLE_GC_CACAO) +bool threads_resume_thread(threadobject *thread) +{ + /* 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(); + + /* release the suspendmutex */ + thread->suspendmutex->unlock(); + + return true; +} +#endif + + +/* threads_join_all_threads **************************************************** + + Join all non-daemon threads. + +*******************************************************************************/ + +void threads_join_all_threads(void) +{ + threadobject *t; + + /* get current thread */ + + t = THREADOBJECT; + + /* This thread is waiting for all non-daemon threads to exit. */ + + thread_set_state_waiting(t); + + /* enter join mutex */ + + threads_mutex_join_lock(); + + /* Wait for condition as long as we have non-daemon threads. We + compare against 1 because the current (main thread) is also a + non-daemon thread. */ + + while (threadlist_get_non_daemons() > 1) + cond_join->wait(mutex_join); + + /* leave join mutex */ + + threads_mutex_join_unlock(); +} + + +/* threads_timespec_earlier **************************************************** + + Return true if timespec tv1 is earlier than timespec tv2. + + IN: + tv1..........first timespec + tv2..........second timespec + + RETURN VALUE: + true, if the first timespec is earlier + +*******************************************************************************/ + +static inline bool threads_timespec_earlier(const struct timespec *tv1, + const struct timespec *tv2) +{ + return (tv1->tv_sec < tv2->tv_sec) + || + (tv1->tv_sec == tv2->tv_sec && tv1->tv_nsec < tv2->tv_nsec); +} + + +/* threads_current_time_is_earlier_than **************************************** + + Check if the current time is earlier than the given timespec. + + IN: + tv...........the timespec to compare against + + RETURN VALUE: + true, if the current time is earlier + +*******************************************************************************/ + +static bool threads_current_time_is_earlier_than(const struct timespec *tv) +{ + struct timeval tvnow; + struct timespec tsnow; + + /* get current time */ + + if (gettimeofday(&tvnow, NULL) != 0) + vm_abort("gettimeofday failed: %s\n", strerror(errno)); + + /* convert it to a timespec */ + + tsnow.tv_sec = tvnow.tv_sec; + tsnow.tv_nsec = tvnow.tv_usec * 1000; + + /* compare current time with the given timespec */ + + return threads_timespec_earlier(&tsnow, tv); +} + + +/* threads_wait_with_timeout *************************************************** + + Wait until the given point in time on a monitor until either + we are notified, we are interrupted, or the time is up. + + IN: + t............the current thread + wakeupTime...absolute (latest) wakeup time + If both tv_sec and tv_nsec are zero, this function + waits for an unlimited amount of time. + +*******************************************************************************/ + +static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTime) +{ + // Acquire the waitmutex. + t->waitmutex->lock(); + + /* wait on waitcond */ + + if (wakeupTime->tv_sec || wakeupTime->tv_nsec) { + /* with timeout */ + while (!t->interrupted && !t->signaled + && threads_current_time_is_earlier_than(wakeupTime)) + { + thread_set_state_timed_waiting(t); + + t->waitcond->timedwait(t->waitmutex, wakeupTime); + + thread_set_state_runnable(t); + } + } + else { + /* no timeout */ + while (!t->interrupted && !t->signaled) { + thread_set_state_waiting(t); + + t->waitcond->wait(t->waitmutex); + + thread_set_state_runnable(t); + } + } + + // Release the waitmutex. + t->waitmutex->unlock(); +} + + +/* threads_wait_with_timeout_relative ****************************************** + + Wait for the given maximum amount of time on a monitor until either + we are notified, we are interrupted, or the time is up. + + IN: + t............the current thread + millis.......milliseconds to wait + nanos........nanoseconds to wait + +*******************************************************************************/ + +void threads_wait_with_timeout_relative(threadobject *thread, s8 millis, + s4 nanos) +{ + struct timespec wakeupTime; + + /* calculate the the (latest) wakeup time */ + + threads_calc_absolute_time(&wakeupTime, millis, nanos); + + /* wait */ + + threads_wait_with_timeout(thread, &wakeupTime); +} + + +/* threads_calc_absolute_time ************************************************** + + Calculate the absolute point in time a given number of ms and ns from now. + + IN: + millis............milliseconds from now + nanos.............nanoseconds from now + + OUT: + *tm...............receives the timespec of the absolute point in time + +*******************************************************************************/ + +static void threads_calc_absolute_time(struct timespec *tm, s8 millis, s4 nanos) +{ + if ((millis != 0x7fffffffffffffffLLU) && (millis || nanos)) { + struct timeval tv; + long nsec; + gettimeofday(&tv, NULL); + tv.tv_sec += millis / 1000; + millis %= 1000; + nsec = tv.tv_usec * 1000 + (s4) millis * 1000000 + nanos; + tm->tv_sec = tv.tv_sec + nsec / 1000000000; + tm->tv_nsec = nsec % 1000000000; + } + else { + tm->tv_sec = 0; + tm->tv_nsec = 0; + } +} + + +/* threads_thread_interrupt **************************************************** + + Interrupt the given thread. + + The thread gets the "waitcond" signal and + its interrupted flag is set to true. + + IN: + thread............the thread to interrupt + +*******************************************************************************/ + +void threads_thread_interrupt(threadobject *t) +{ +#if defined(__LINUX__) && defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) + /* See openjdk/jdk/src/solaris/native/java/net/linux_close.c, "sigWakeup" */ + int sig = (__SIGRTMAX - 2); +#else + int sig = SIGHUP; +#endif + /* Signal the thread a "waitcond" and tell it that it has been + interrupted. */ + + t->waitmutex->lock(); + + DEBUGTHREADS("interrupted", t); + + /* Interrupt blocking system call using a signal. */ + + pthread_kill(t->tid, sig); + + t->waitcond->signal(); + + t->interrupted = true; + + t->waitmutex->unlock(); +} + + +/* threads_sleep *************************************************************** + + Sleep the current thread for the specified amount of time. + +*******************************************************************************/ + +void threads_sleep(int64_t millis, int32_t nanos) +{ + threadobject *t; + struct timespec wakeupTime; + bool interrupted; + + if (millis < 0) { +/* exceptions_throw_illegalargumentexception("timeout value is negative"); */ + exceptions_throw_illegalargumentexception(); + return; + } + + t = thread_get_current(); + + if (thread_is_interrupted(t) && !exceptions_get_exception()) { + /* Clear interrupted flag (Mauve test: + gnu/testlet/java/lang/Thread/interrupt). */ + + thread_set_interrupted(t, false); + +/* exceptions_throw_interruptedexception("sleep interrupted"); */ + exceptions_throw_interruptedexception(); + return; + } + + threads_calc_absolute_time(&wakeupTime, millis, nanos); + + threads_wait_with_timeout(t, &wakeupTime); + + interrupted = thread_is_interrupted(t); + + if (interrupted) { + thread_set_interrupted(t, false); + + /* An other exception could have been thrown + (e.g. ThreadDeathException). */ + + if (!exceptions_get_exception()) + exceptions_throw_interruptedexception(); + } +} + + +/* threads_yield *************************************************************** + + Yield to the scheduler. + +*******************************************************************************/ + +void threads_yield(void) +{ + sched_yield(); +} + +#if defined(ENABLE_TLH) + +void threads_tlh_add_frame() { + tlh_add_frame(&(THREADOBJECT->tlh)); +} + +void threads_tlh_remove_frame() { + tlh_remove_frame(&(THREADOBJECT->tlh)); +} + +#endif + +} // extern "C" + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c++ + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/threads/posix/thread-posix.h b/src/threads/posix/thread-posix.h deleted file mode 100644 index 5449cadfa..000000000 --- a/src/threads/posix/thread-posix.h +++ /dev/null @@ -1,322 +0,0 @@ -/* src/threads/posix/thread-posix.h - POSIX thread functions - - Copyright (C) 1996-2005, 2006, 2007, 2008 - CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO - - This file is part of CACAO. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - -*/ - - -#ifndef _THREAD_POSIX_H -#define _THREAD_POSIX_H - -/* forward typedefs ***********************************************************/ - -typedef struct threadobject threadobject; - - -#include "config.h" - -#include -#include - -#include "vm/types.h" - -#include "mm/memory.h" - -#if defined(ENABLE_TLH) -#include "mm/tlh.h" -#endif - -#include "native/localref.h" - -#include "threads/condition.hpp" -#include "threads/mutex.hpp" - -#include "threads/posix/lock.h" - -#include "vm/global.h" -#include "vm/vm.hpp" - -#if defined(ENABLE_GC_CACAO) -# include "vm/jit/executionstate.h" -# include "vm/jit/replace.h" -#endif - -#include "vm/jit/stacktrace.hpp" - -#if defined(ENABLE_INTRP) -#include "vm/jit/intrp/intrp.h" -#endif - -#if defined(__DARWIN__) -# include - -typedef struct { - mutex_t mutex; - pthread_cond_t cond; - int value; -} sem_t; - -#else -# include -#endif - - - -/* current threadobject *******************************************************/ - -#if defined(HAVE___THREAD) - -#define THREADOBJECT thread_current - -extern __thread threadobject *thread_current; - -#else /* defined(HAVE___THREAD) */ - -#define THREADOBJECT \ - ((threadobject *) pthread_getspecific(thread_current_key)) - -extern pthread_key_t thread_current_key; - -#endif /* defined(HAVE___THREAD) */ - - -/* threadobject **************************************************************** - - Struct holding thread local variables. - -*******************************************************************************/ - -#define THREAD_FLAG_JAVA 0x01 /* a normal Java thread */ -#define THREAD_FLAG_INTERNAL 0x02 /* CACAO internal thread */ -#define THREAD_FLAG_DAEMON 0x04 /* daemon thread */ -#define THREAD_FLAG_IN_NATIVE 0x08 /* currently executing native code */ - -#define SUSPEND_REASON_JNI 1 /* suspended from JNI */ -#define SUSPEND_REASON_STOPWORLD 2 /* suspended from stop-thw-world */ - - -struct threadobject { - java_object_t *object; /* link to java.lang.Thread object */ - - ptrint thinlock; /* pre-computed thin lock value */ - - s4 index; /* thread index, starting with 1 */ - u4 flags; /* flag field */ - u4 state; /* state field */ - - pthread_t tid; /* pthread id */ - -#if defined(__DARWIN__) - mach_port_t mach_thread; /* Darwin thread id */ -#endif - - /* for the sable tasuki lock extension */ - bool flc_bit; - struct threadobject *flc_list; /* FLC list head for this thread */ - struct threadobject *flc_next; /* next pointer for FLC list */ - java_handle_t *flc_object; - Mutex* flc_lock; /* controlling access to these fields */ - Condition* flc_cond; - - /* these are used for the wait/notify implementation */ - Mutex* waitmutex; - Condition* waitcond; - - Mutex* suspendmutex; /* lock before suspending this thread */ - Condition* suspendcond; /* notify to resume this thread */ - - bool interrupted; - bool signaled; - - bool suspended; /* is this thread suspended? */ - s4 suspend_reason; /* reason for suspending */ - - u1 *pc; /* current PC (used for profiling) */ - - java_object_t *_exceptionptr; /* current exception */ - stackframeinfo_t *_stackframeinfo; /* current native stackframeinfo */ - localref_table *_localref_table; /* JNI local references */ - -#if defined(ENABLE_INTRP) - Cell *_global_sp; /* stack pointer for interpreter */ -#endif - -#if defined(ENABLE_GC_CACAO) - bool gc_critical; /* indicates a critical section */ - - sourcestate_t *ss; - executionstate_t *es; -#endif - - dumpinfo_t dumpinfo; /* dump memory info structure */ - -#if defined(ENABLE_DEBUG_FILTER) - u2 filterverbosecallctr[2]; /* counters for verbose call filter */ -#endif - -#if !defined(NDEBUG) - s4 tracejavacallindent; - u4 tracejavacallcount; -#endif - -#if defined(ENABLE_TLH) - tlh_t tlh; -#endif - -#if defined(ENABLE_ESCAPE_REASON) - void *escape_reasons; -#endif - - listnode_t linkage; /* threads-list */ - listnode_t linkage_free; /* free-list */ -}; - - -/* native-world flags *********************************************************/ - -#if defined(ENABLE_GC_CACAO) -# define THREAD_NATIVEWORLD_ENTER THREADOBJECT->flags |= THREAD_FLAG_IN_NATIVE -# define THREAD_NATIVEWORLD_EXIT THREADOBJECT->flags &= ~THREAD_FLAG_IN_NATIVE -#else -# define THREAD_NATIVEWORLD_ENTER /*nop*/ -# define THREAD_NATIVEWORLD_EXIT /*nop*/ -#endif - - -/* counter for verbose call filter ********************************************/ - -#if defined(ENABLE_DEBUG_FILTER) -# define FILTERVERBOSECALLCTR (THREADOBJECT->filterverbosecallctr) -#endif - -/* state for trace java call **************************************************/ - -#if !defined(NDEBUG) -# define TRACEJAVACALLINDENT (THREADOBJECT->tracejavacallindent) -# define TRACEJAVACALLCOUNT (THREADOBJECT->tracejavacallcount) -#endif - - -/* inline functions ***********************************************************/ - -/* thread_get_current ********************************************************** - - Return the threadobject of the current thread. - - RETURN: - the current threadobject * - -*******************************************************************************/ - -inline static threadobject *thread_get_current(void) -{ - threadobject *t; - -#if defined(HAVE___THREAD) - t = thread_current; -#else - t = (threadobject *) pthread_getspecific(thread_current_key); -#endif - - return t; -} - - -/* thread_set_current ********************************************************** - - Set the current thread object. - - IN: - t ... the thread object to set - -*******************************************************************************/ - -inline static void thread_set_current(threadobject *t) -{ -#if defined(HAVE___THREAD) - thread_current = t; -#else - int result; - - result = pthread_setspecific(thread_current_key, t); - - if (result != 0) - vm_abort_errnum(result, "thread_set_current: pthread_setspecific failed"); -#endif -} - - -inline static stackframeinfo_t *threads_get_current_stackframeinfo(void) -{ - return THREADOBJECT->_stackframeinfo; -} - -inline static void threads_set_current_stackframeinfo(stackframeinfo_t *sfi) -{ - THREADOBJECT->_stackframeinfo = sfi; -} - - -/* functions ******************************************************************/ - -void threads_sem_init(sem_t *sem, bool shared, int value); -void threads_sem_wait(sem_t *sem); -void threads_sem_post(sem_t *sem); - -void threads_start_thread(threadobject *thread, functionptr function); - -void threads_set_thread_priority(pthread_t tid, int priority); - -#if defined(ENABLE_GC_CACAO) -bool threads_suspend_thread(threadobject *thread, s4 reason); -void threads_suspend_ack(u1* pc, u1* sp); -bool threads_resume_thread(threadobject *thread); -#endif - -void threads_join_all_threads(void); - -void threads_sleep(int64_t millis, int32_t nanos); - -void threads_wait_with_timeout_relative(threadobject *t, s8 millis, s4 nanos); - -void threads_thread_interrupt(threadobject *thread); - -#if defined(ENABLE_TLH) -void threads_tlh_add_frame(); -void threads_tlh_remove_frame(); -#endif - -#endif /* _THREAD_POSIX_H */ - - -/* - * These are local overrides for various environment variables in Emacs. - * Please do not remove this and leave it at the end of the file, where - * Emacs will automagically detect them. - * --------------------------------------------------------------------- - * Local variables: - * mode: c - * indent-tabs-mode: t - * c-basic-offset: 4 - * tab-width: 4 - * End: - * vim:noexpandtab:sw=4:ts=4: - */ diff --git a/src/threads/posix/thread-posix.hpp b/src/threads/posix/thread-posix.hpp new file mode 100644 index 000000000..452322305 --- /dev/null +++ b/src/threads/posix/thread-posix.hpp @@ -0,0 +1,330 @@ +/* src/threads/posix/thread-posix.hpp - POSIX thread functions + + Copyright (C) 1996-2005, 2006, 2007, 2008 + CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +*/ + + +#ifndef _THREAD_POSIX_HPP +#define _THREAD_POSIX_HPP + +/* forward typedefs ***********************************************************/ + +typedef struct threadobject threadobject; + + +#include "config.h" + +#include +#include + +#include "vm/types.h" + +#include "mm/memory.h" + +#if defined(ENABLE_TLH) +#include "mm/tlh.h" +#endif + +#include "native/localref.h" + +#include "threads/condition.hpp" +#include "threads/mutex.hpp" + +#include "threads/posix/lock.h" + +#include "vm/global.h" +#include "vm/vm.hpp" + +#if defined(ENABLE_GC_CACAO) +# include "vm/jit/executionstate.h" +# include "vm/jit/replace.h" +#endif + +#include "vm/jit/stacktrace.hpp" + +#if defined(ENABLE_INTRP) +#include "vm/jit/intrp/intrp.h" +#endif + +#if defined(__DARWIN__) +# include + +typedef struct { + mutex_t mutex; + pthread_cond_t cond; + int value; +} sem_t; + +#else +# include +#endif + + +// FIXME +#ifdef __cplusplus +extern "C" { +#endif + +/* current threadobject *******************************************************/ + +#if defined(HAVE___THREAD) + +#define THREADOBJECT thread_current + +extern __thread threadobject *thread_current; + +#else /* defined(HAVE___THREAD) */ + +#define THREADOBJECT \ + ((threadobject *) pthread_getspecific(thread_current_key)) + +extern pthread_key_t thread_current_key; + +#endif /* defined(HAVE___THREAD) */ + + +/* threadobject **************************************************************** + + Struct holding thread local variables. + +*******************************************************************************/ + +#define THREAD_FLAG_JAVA 0x01 /* a normal Java thread */ +#define THREAD_FLAG_INTERNAL 0x02 /* CACAO internal thread */ +#define THREAD_FLAG_DAEMON 0x04 /* daemon thread */ +#define THREAD_FLAG_IN_NATIVE 0x08 /* currently executing native code */ + +#define SUSPEND_REASON_JNI 1 /* suspended from JNI */ +#define SUSPEND_REASON_STOPWORLD 2 /* suspended from stop-thw-world */ + + +struct threadobject { + java_object_t *object; /* link to java.lang.Thread object */ + + ptrint thinlock; /* pre-computed thin lock value */ + + s4 index; /* thread index, starting with 1 */ + u4 flags; /* flag field */ + u4 state; /* state field */ + + pthread_t tid; /* pthread id */ + +#if defined(__DARWIN__) + mach_port_t mach_thread; /* Darwin thread id */ +#endif + + /* for the sable tasuki lock extension */ + bool flc_bit; + struct threadobject *flc_list; /* FLC list head for this thread */ + struct threadobject *flc_next; /* next pointer for FLC list */ + java_handle_t *flc_object; + Mutex* flc_lock; /* controlling access to these fields */ + Condition* flc_cond; + + /* these are used for the wait/notify implementation */ + Mutex* waitmutex; + Condition* waitcond; + + Mutex* suspendmutex; /* lock before suspending this thread */ + Condition* suspendcond; /* notify to resume this thread */ + + bool interrupted; + bool signaled; + + bool suspended; /* is this thread suspended? */ + s4 suspend_reason; /* reason for suspending */ + + u1 *pc; /* current PC (used for profiling) */ + + java_object_t *_exceptionptr; /* current exception */ + stackframeinfo_t *_stackframeinfo; /* current native stackframeinfo */ + localref_table *_localref_table; /* JNI local references */ + +#if defined(ENABLE_INTRP) + Cell *_global_sp; /* stack pointer for interpreter */ +#endif + +#if defined(ENABLE_GC_CACAO) + bool gc_critical; /* indicates a critical section */ + + sourcestate_t *ss; + executionstate_t *es; +#endif + + dumpinfo_t dumpinfo; /* dump memory info structure */ + +#if defined(ENABLE_DEBUG_FILTER) + u2 filterverbosecallctr[2]; /* counters for verbose call filter */ +#endif + +#if !defined(NDEBUG) + s4 tracejavacallindent; + u4 tracejavacallcount; +#endif + +#if defined(ENABLE_TLH) + tlh_t tlh; +#endif + +#if defined(ENABLE_ESCAPE_REASON) + void *escape_reasons; +#endif + + listnode_t linkage; /* threads-list */ + listnode_t linkage_free; /* free-list */ +}; + + +/* native-world flags *********************************************************/ + +#if defined(ENABLE_GC_CACAO) +# define THREAD_NATIVEWORLD_ENTER THREADOBJECT->flags |= THREAD_FLAG_IN_NATIVE +# define THREAD_NATIVEWORLD_EXIT THREADOBJECT->flags &= ~THREAD_FLAG_IN_NATIVE +#else +# define THREAD_NATIVEWORLD_ENTER /*nop*/ +# define THREAD_NATIVEWORLD_EXIT /*nop*/ +#endif + + +/* counter for verbose call filter ********************************************/ + +#if defined(ENABLE_DEBUG_FILTER) +# define FILTERVERBOSECALLCTR (THREADOBJECT->filterverbosecallctr) +#endif + +/* state for trace java call **************************************************/ + +#if !defined(NDEBUG) +# define TRACEJAVACALLINDENT (THREADOBJECT->tracejavacallindent) +# define TRACEJAVACALLCOUNT (THREADOBJECT->tracejavacallcount) +#endif + + +/* inline functions ***********************************************************/ + +/* thread_get_current ********************************************************** + + Return the threadobject of the current thread. + + RETURN: + the current threadobject * + +*******************************************************************************/ + +inline static threadobject *thread_get_current(void) +{ + threadobject *t; + +#if defined(HAVE___THREAD) + t = thread_current; +#else + t = (threadobject *) pthread_getspecific(thread_current_key); +#endif + + return t; +} + + +/* thread_set_current ********************************************************** + + Set the current thread object. + + IN: + t ... the thread object to set + +*******************************************************************************/ + +inline static void thread_set_current(threadobject *t) +{ +#if defined(HAVE___THREAD) + thread_current = t; +#else + int result; + + result = pthread_setspecific(thread_current_key, t); + + if (result != 0) + vm_abort_errnum(result, "thread_set_current: pthread_setspecific failed"); +#endif +} + + +inline static stackframeinfo_t *threads_get_current_stackframeinfo(void) +{ + return THREADOBJECT->_stackframeinfo; +} + +inline static void threads_set_current_stackframeinfo(stackframeinfo_t *sfi) +{ + THREADOBJECT->_stackframeinfo = sfi; +} + + +/* functions ******************************************************************/ + +void threads_sem_init(sem_t *sem, bool shared, int value); +void threads_sem_wait(sem_t *sem); +void threads_sem_post(sem_t *sem); + +void threads_start_thread(threadobject *thread, functionptr function); + +void threads_set_thread_priority(pthread_t tid, int priority); + +#if defined(ENABLE_GC_CACAO) +bool threads_suspend_thread(threadobject *thread, s4 reason); +void threads_suspend_ack(u1* pc, u1* sp); +bool threads_resume_thread(threadobject *thread); +#endif + +void threads_join_all_threads(void); + +void threads_sleep(int64_t millis, int32_t nanos); + +void threads_wait_with_timeout_relative(threadobject *t, s8 millis, s4 nanos); + +void threads_thread_interrupt(threadobject *thread); + +#if defined(ENABLE_TLH) +void threads_tlh_add_frame(); +void threads_tlh_remove_frame(); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _THREAD_POSIX_HPP + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c++ + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/threads/thread.h b/src/threads/thread.h index ba535dfca..20a956566 100644 --- a/src/threads/thread.h +++ b/src/threads/thread.h @@ -37,7 +37,7 @@ extern "C" { #include "threads/mutex.hpp" #if defined(ENABLE_THREADS) -# include "threads/posix/thread-posix.h" +# include "threads/posix/thread-posix.hpp" #else # include "threads/none/thread-none.h" #endif