-#include "global.h"
+/* src/threads/native/threads.c - native threads support
+
+ Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
+ R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
+ C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
+ Institut f. Computersprachen - TU Wien
+
+ 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., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ Contact: cacao@complang.tuwien.ac.at
+
+ Authors: Stefan Ring
+
+ Changes: Christian Thalinger
+
+ $Id: threads.c 3570 2005-11-04 16:58:36Z motse $
+
+*/
-#if defined(NATIVE_THREADS)
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+
#include "config.h"
-#include "thread.h"
-#include "codegen.h"
-#include "locks.h"
-#include "tables.h"
-#include "native.h"
-#include "loader.h"
-#include "builtin.h"
-#include "asmpart.h"
-#include "toolbox/logging.h"
-#include "toolbox/memory.h"
-#include "toolbox/avl.h"
-#include "mm/boehm.h"
+#include "vm/types.h"
-#include "nat/java_lang_Object.h"
-#include "nat/java_lang_Throwable.h"
-#include "nat/java_lang_Thread.h"
-#include "nat/java_lang_ThreadGroup.h"
+#ifndef USE_MD_THREAD_STUFF
+#include "machine-instr.h"
+#else
+#include "threads/native/generic-primitives.h"
+#endif
-#include <pthread.h>
-#include <semaphore.h>
+#include "cacao/cacao.h"
+#include "mm/boehm.h"
+#include "mm/memory.h"
+#include "native/native.h"
+#include "native/include/java_lang_Object.h"
+#include "native/include/java_lang_Throwable.h"
+#include "native/include/java_lang_Thread.h"
+#include "native/include/java_lang_ThreadGroup.h"
+#include "native/include/java_lang_VMThread.h"
+#include "threads/native/threads.h"
+#include "toolbox/avl.h"
+#include "toolbox/logging.h"
+#include "vm/builtin.h"
+#include "vm/exceptions.h"
+#include "vm/global.h"
+#include "vm/loader.h"
+#include "vm/options.h"
+#include "vm/stringlocal.h"
+#include "vm/tables.h"
+#include "vm/jit/asmpart.h"
+#if !defined(__DARWIN__)
#if defined(__LINUX__)
#define GC_LINUX_THREADS
-#include "../mm/boehm-gc/include/gc.h"
+#elif defined(__MIPS__)
+#define GC_IRIX_THREADS
+#endif
+#include "boehm-gc/include/gc.h"
+#endif
+
+#ifdef USE_MD_THREAD_STUFF
+pthread_mutex_t _atomic_add_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t _cas_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t _mb_lock = PTHREAD_MUTEX_INITIALIZER;
#endif
#ifdef MUTEXSIM
+/* We need this for older MacOSX (10.1.x) */
+
typedef struct {
pthread_mutex_t mutex;
pthread_t owner;
#endif /* MUTEXSIM */
-#include "machine-instr.h"
+static void setPriority(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);
+}
+
static struct avl_table *criticaltree;
-static threadobject *mainthreadobj;
+threadobject *mainthreadobj;
#ifndef HAVE___THREAD
pthread_key_t tkey_threadinfo;
volatile int stopworldwhere;
static sem_t suspend_ack;
+#if defined(__MIPS__)
+static pthread_mutex_t suspend_ack_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t suspend_cond = PTHREAD_COND_INITIALIZER;
+#endif
/*
* where - 1 from within GC
kern_return_t r;
r = thread_suspend(thread);
- if (r != KERN_SUCCESS)
- panic("thread_suspend failed");
+ if (r != KERN_SUCCESS) {
+ log_text("thread_suspend failed");
+ assert(0);
+ }
r = thread_get_state(thread, flavor,
(natural_t*)&thread_state, &thread_state_count);
- if (r != KERN_SUCCESS)
- panic("thread_get_state failed");
+ if (r != KERN_SUCCESS) {
+ log_text("thread_get_state failed");
+ assert(0);
+ }
thread_restartcriticalsection(&thread_state);
r = thread_set_state(thread, flavor,
(natural_t*)&thread_state, thread_state_count);
- if (r != KERN_SUCCESS)
- panic("thread_set_state failed");
+ if (r != KERN_SUCCESS) {
+ log_text("thread_set_state failed");
+ assert(0);
+ }
}
tobj = tobj->info.next;
} while (tobj != mainthreadobj);
nativethread *info = &tobj->info;
if (info != infoself)
{
+ mach_port_t thread = info->mach_thread;
+ kern_return_t r;
+
r = thread_resume(thread);
- if (r != KERN_SUCCESS)
- panic("thread_resume failed");
+ if (r != KERN_SUCCESS) {
+ log_text("thread_resume failed");
+ assert(0);
+ }
}
tobj = tobj->info.next;
} while (tobj != mainthreadobj);
#endif
+#if defined(__MIPS__)
+static void cast_irixresume()
+{
+ pthread_mutex_lock(&suspend_ack_lock);
+ pthread_cond_broadcast(&suspend_cond);
+ pthread_mutex_unlock(&suspend_ack_lock);
+}
+#endif
+
void cast_stopworld()
{
int count, i;
pthread_mutex_lock(&threadlistlock);
#if defined(__DARWIN__)
cast_darwinresume();
+#elif defined(__MIPS__)
+ cast_irixresume();
#else
cast_sendsignals(GC_signum2(), -1);
#endif
thread_restartcriticalsection(ctx);
+ /* Do as Boehm does. On IRIX a condition variable is used for wake-up
+ (not POSIX async-safe). */
+#if defined(__IRIX__)
+ pthread_mutex_lock(&suspend_ack_lock);
+ sem_post(&suspend_ack);
+ pthread_cond_wait(&suspend_cond, &suspend_ack_lock);
+ pthread_mutex_unlock(&suspend_ack_lock);
+#else
sem_post(&suspend_ack);
sig = GC_signum2();
sigfillset(&sigs);
sigdelset(&sigs, sig);
sigsuspend(&sigs);
+#endif
}
int cacao_suspendhandler(ucontext_t *ctx)
#endif
}
-/*
- * Initialize threads.
- */
-void
-initThreadsEarly()
+
+/* thread_setself **************************************************************
+
+ XXX
+
+*******************************************************************************/
+
+void *thread_getself(void)
+{
+ return pthread_getspecific(tkey_threadinfo);
+}
+
+
+static monitorLockRecord *dummyLR;
+
+static void initPools();
+
+
+/* thread_preinit **************************************************************
+
+ Do some early initialization of stuff required.
+
+*******************************************************************************/
+
+void threads_preinit(void)
{
#ifndef MUTEXSIM
pthread_mutexattr_t mutexattr;
pthread_mutex_init(&threadlistlock, NULL);
pthread_mutex_init(&stopworldlock, NULL);
- /* Allocate something so the garbage collector's signal handlers are */
- /* installed. */
+ /* Allocate something so the garbage collector's signal handlers
+ are installed. */
heap_allocate(1, false, NULL);
mainthreadobj = NEW(threadobject);
- memset(mainthreadobj, 0, sizeof(threadobject));
+ mainthreadobj->info.tid = pthread_self();
#if !defined(HAVE___THREAD)
pthread_key_create(&tkey_threadinfo, NULL);
#endif
setthreadobject(mainthreadobj);
+ initPools();
criticaltree = avl_create(criticalcompare, NULL, NULL);
thread_addstaticcritical();
sem_init(&suspend_ack, 0, 0);
+
+ /* Every newly created object's monitorPtr points here so we save
+ a check against NULL */
+ dummyLR = NEW(monitorLockRecord);
+ dummyLR->o = NULL;
+ dummyLR->ownerThread = NULL;
+ dummyLR->waiting = false;
}
static pthread_attr_t threadattr;
+
static void freeLockRecordPools(lockRecordPool *);
-void
-initThreads(u1 *stackbottom)
+
+/* threads_init ****************************************************************
+
+ Initializes the threads required by the JVM: main, finalizer.
+
+*******************************************************************************/
+
+bool threads_init(u1 *stackbottom)
{
- classinfo *threadclass;
- classinfo *threadgroupclass;
- java_lang_Thread *mainthread;
- threadobject *tempthread = mainthreadobj;
+ java_lang_String *threadname;
+ java_lang_Thread *mainthread;
+ java_lang_ThreadGroup *threadgroup;
+ threadobject *tempthread;
+ methodinfo *method;
- threadclass = class_new(utf_new_char("java/lang/Thread"));
- class_load(threadclass);
- class_link(threadclass);
+ tempthread = mainthreadobj;
- assert(threadclass);
freeLockRecordPools(mainthreadobj->ee.lrpool);
- threadclass->instancesize = sizeof(threadobject);
- mainthreadobj = (threadobject *) builtin_new(threadclass);
- assert(mainthreadobj);
+ /* This is kinda tricky, we grow the java.lang.Thread object so we
+ can keep the execution environment there. No Thread object must
+ have been created at an earlier time. */
+
+ class_java_lang_VMThread->instancesize = sizeof(threadobject);
+
+ /* create a VMThread */
+
+ mainthreadobj = (threadobject *) builtin_new(class_java_lang_VMThread);
+
+ if (!mainthreadobj)
+ return false;
FREE(tempthread, threadobject);
+
initThread(&mainthreadobj->o);
-#if !defined(HAVE___THREAD)
- pthread_setspecific(tkey_threadinfo, mainthreadobj);
-#else
- threadobj = mainthreadobj;
-#endif
+ setthreadobject(mainthreadobj);
- mainthread = &mainthreadobj->o;
initLocks();
+
mainthreadobj->info.next = mainthreadobj;
mainthreadobj->info.prev = mainthreadobj;
- mainthread->name=javastring_new(utf_new_char("main"));
+#if defined(ENABLE_INTRP)
+ /* create interpreter stack */
+
+ if (opt_intrp) {
+ MSET(intrp_main_stack, 0, u1, opt_stacksize);
+ mainthreadobj->info._global_sp = intrp_main_stack + opt_stacksize;
+ }
+#endif
+
+ threadname = javastring_new(utf_new_char("main"));
+
+ /* allocate and init ThreadGroup */
+
+ threadgroup = (java_lang_ThreadGroup *)
+ native_new_and_init(class_java_lang_ThreadGroup);
+
+ if (!threadgroup)
+ throw_exception_exit();
+
+ /* create a Thread */
- /* Allocate and init ThreadGroup */
- threadgroupclass = class_new(utf_new_char("java/lang/ThreadGroup"));
- class_load(threadgroupclass);
- class_link(threadgroupclass);
+ mainthread = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
- mainthread->group =
- (java_lang_ThreadGroup *) native_new_and_init(threadgroupclass);
- assert(mainthread->group != 0);
+ if (!mainthread)
+ throw_exception_exit();
+
+ mainthreadobj->o.thread = mainthread;
+
+ /* call Thread.<init>(Ljava/lang/VMThread;Ljava/lang/String;IZ)V */
+
+ method = class_resolveclassmethod(class_java_lang_Thread,
+ utf_init,
+ utf_new_char("(Ljava/lang/VMThread;Ljava/lang/String;IZ)V"),
+ class_java_lang_Thread,
+ true);
+
+ if (!method)
+ return false;
+
+ asm_calljavafunction(method, mainthread, mainthreadobj, threadname, (void*) 5);
+ if (*exceptionptr)
+ return false;
+
+ mainthread->group = threadgroup;
+ /* XXX This is a hack because the fourth argument was omitted */
+ mainthread->daemon = false;
+
+ /* add mainthread to ThreadGroup */
+
+ method = class_resolveclassmethod(class_java_lang_ThreadGroup,
+ utf_new_char("addThread"),
+ utf_new_char("(Ljava/lang/Thread;)V"),
+ class_java_lang_ThreadGroup,
+ true);
+
+ if (!method)
+ return false;
+
+ asm_calljavafunction(method, threadgroup, mainthread, NULL, NULL);
+
+ if (*exceptionptr)
+ return false;
+
+ setPriority(pthread_self(), 5);
pthread_attr_init(&threadattr);
pthread_attr_setdetachstate(&threadattr, PTHREAD_CREATE_DETACHED);
+
+ /* everything's ok */
+
+ return true;
}
-void initThread(java_lang_Thread *t)
+
+void initThread(java_lang_VMThread *t)
{
- nativethread *info = &((threadobject*) t)->info;
+ threadobject *thread = (threadobject*) t;
+ nativethread *info = &thread->info;
+ info->tid = pthread_self();
+ /* TODO destroy all those things */
pthread_mutex_init(&info->joinMutex, NULL);
pthread_cond_init(&info->joinCond, NULL);
+
+ pthread_mutex_init(&thread->waitLock, NULL);
+ pthread_cond_init(&thread->waitCond, NULL);
+ thread->interrupted = false;
+ thread->signaled = false;
+ thread->isSleeping = false;
}
static void initThreadLocks(threadobject *);
+
typedef struct {
threadobject *thread;
- sem_t *psem;
+ functionptr function;
+ sem_t *psem;
+ sem_t *psem_first;
} startupinfo;
-static void *threadstartup(void *t)
+
+/* threads_startup *************************************************************
+
+ Thread startup function called by pthread_create.
+
+******************************************************************************/
+
+static void *threads_startup_thread(void *t)
{
- startupinfo *startup = t;
- threadobject *thread = startup->thread;
- sem_t *psem = startup->psem;
- nativethread *info = &thread->info;
+ startupinfo *startup;
+ threadobject *thread;
+ sem_t *psem;
+ nativethread *info;
threadobject *tnext;
- methodinfo *method;
+ methodinfo *method;
+ functionptr function;
+
+#if defined(ENABLE_INTRP)
+ u1 *intrp_thread_stack;
+
+ /* create interpreter stack */
+
+ if (opt_intrp) {
+ intrp_thread_stack = (u1 *) alloca(opt_stacksize);
+ MSET(intrp_thread_stack, 0, u1, opt_stacksize);
+ }
+#endif
+
+ /* get passed startupinfo structure and the values in there */
+
+ startup = t;
+
+ thread = startup->thread;
+ function = startup->function;
+ psem = startup->psem;
+
+ info = &thread->info;
+
+ /* Seems like we've encountered a situation where info->tid was not set by
+ * pthread_create. We alleviate this problem by waiting for pthread_create
+ * to return. */
+ sem_wait(startup->psem_first);
t = NULL;
#if defined(__DARWIN__)
#endif
setthreadobject(thread);
+ /* insert the thread into the threadlist */
+
pthread_mutex_lock(&threadlistlock);
+
info->prev = mainthreadobj;
info->next = tnext = mainthreadobj->info.next;
mainthreadobj->info.next = thread;
tnext->info.prev = thread;
+
pthread_mutex_unlock(&threadlistlock);
initThreadLocks(thread);
startup = NULL;
sem_post(psem);
- /* Find the run()V method and call it */
- method = class_findmethod(thread->o.header.vftbl->class,
- utf_new_char("run"), utf_new_char("()V"));
- if (!method)
- panic("Cannot find method \'void run ()\'");
+ setPriority(info->tid, thread->o.thread->priority);
- asm_calljavafunction(method, thread, NULL, NULL, NULL);
+#if defined(ENABLE_INTRP)
+ /* set interpreter stack */
- if (info->_exceptionptr) {
- utf_display_classname((info->_exceptionptr)->vftbl->class->name);
- printf("\n");
- }
+ if (opt_intrp)
+ THREADINFO->_global_sp = (void *) (intrp_thread_stack + opt_stacksize);
+#endif
+
+ /* find and run the Thread.run()V method if no other function was passed */
+
+ if (function == NULL) {
+ method = class_resolveclassmethod(thread->o.header.vftbl->class,
+ utf_run,
+ utf_void__void,
+ thread->o.header.vftbl->class,
+ true);
+
+ if (!method)
+ throw_exception();
+
+ asm_calljavafunction(method, thread, NULL, NULL, NULL);
+
+ } else {
+ /* call passed function, e.g. finalizer_thread */
+
+ (function)();
+ }
+
+ /* Allow lock record pools to be used by other threads. They
+ cannot be deleted so we'd better not waste them. */
freeLockRecordPools(thread->ee.lrpool);
+ /* remove thread from thread list, do this inside a lock */
+
pthread_mutex_lock(&threadlistlock);
info->next->info.prev = info->prev;
info->prev->info.next = info->next;
pthread_mutex_unlock(&threadlistlock);
+ /* reset thread id (lock on joinMutex? TWISTI) */
+
pthread_mutex_lock(&info->joinMutex);
info->tid = 0;
pthread_mutex_unlock(&info->joinMutex);
+
pthread_cond_broadcast(&info->joinCond);
return NULL;
}
-void startThread(threadobject *t)
+
+/* threads_start_thread ********************************************************
+
+ Start a thread in the JVM.
+
+******************************************************************************/
+
+void threads_start_thread(thread *t, functionptr function)
{
- nativethread *info = &t->info;
- sem_t sem;
- startupinfo startup;
+ nativethread *info;
+ sem_t sem;
+ sem_t sem_first;
+ startupinfo startup;
+
+ info = &((threadobject *) t->vmThread)->info;
+
+ /* fill startupinfo structure passed by pthread_create to XXX */
- startup.thread = t;
- startup.psem = &sem;
+ startup.thread = (threadobject*) t->vmThread;
+ startup.function = function; /* maybe we don't call Thread.run()V */
+ startup.psem = &sem;
+ startup.psem_first = &sem_first;
sem_init(&sem, 0, 0);
+ sem_init(&sem_first, 0, 0);
- if (pthread_create(&info->tid, &threadattr, threadstartup, &startup))
- panic("pthread_create failed");
+ if (pthread_create(&info->tid, &threadattr, threads_startup_thread,
+ &startup)) {
+ log_text("pthread_create failed");
+ assert(0);
+ }
+
+ sem_post(&sem_first);
+
+ /* wait here until the thread has entered itself into the thread list */
- /* Wait here until thread has entered itself into the thread list */
sem_wait(&sem);
sem_destroy(&sem);
+ sem_destroy(&sem_first);
+}
+
+
+/* At the end of the program, we wait for all running non-daemon threads to die
+ */
+
+static threadobject *findNonDaemon(threadobject *thread)
+{
+ while (thread != mainthreadobj) {
+ if (!thread->o.thread->daemon)
+ return thread;
+ thread = thread->info.prev;
+ }
+
+ return NULL;
}
void joinAllThreads()
{
+ threadobject *thread;
pthread_mutex_lock(&threadlistlock);
- while (mainthreadobj->info.prev != mainthreadobj) {
- nativethread *info = &mainthreadobj->info.prev->info;
+ while ((thread = findNonDaemon(mainthreadobj->info.prev)) != NULL) {
+ nativethread *info = &thread->info;
pthread_mutex_lock(&info->joinMutex);
pthread_mutex_unlock(&threadlistlock);
- if (info->tid)
+ while (info->tid)
pthread_cond_wait(&info->joinCond, &info->joinMutex);
pthread_mutex_unlock(&info->joinMutex);
pthread_mutex_lock(&threadlistlock);
pthread_mutex_unlock(&threadlistlock);
}
-bool aliveThread(java_lang_Thread *t)
-{
- return ((threadobject*) t)->info.tid != 0;
-}
-
-void sleepThread(s8 millis)
-{
- struct timespec tv;
- tv.tv_sec = millis / 1000;
- tv.tv_nsec = millis % 1000 * 1000000;
- do { } while (nanosleep(&tv, &tv) == EINTR);
-}
-
-void yieldThread()
-{
- sched_yield();
-}
-
-static void timedCondWait(pthread_cond_t *cond, pthread_mutex_t *mutex, s8 millis)
+static void initLockRecord(monitorLockRecord *r, threadobject *t)
{
- struct timeval now;
- struct timespec desttime;
- gettimeofday(&now, NULL);
- desttime.tv_sec = millis / 1000;
- desttime.tv_nsec = millis % 1000 * 1000000;
- pthread_cond_timedwait(cond, mutex, &desttime);
+ r->lockCount = 1;
+ r->ownerThread = t;
+ r->queuers = 0;
+ r->o = NULL;
+ r->waiter = NULL;
+ r->incharge = (monitorLockRecord *) &dummyLR;
+ r->waiting = false;
+ sem_init(&r->queueSem, 0, 0);
+ pthread_mutex_init(&r->resolveLock, NULL);
+ pthread_cond_init(&r->resolveWait, NULL);
}
+/* No lock record must ever be destroyed because there may still be references
+ * to it.
-#define NEUTRAL 0
-#define LOCKED 1
-#define WAITERS 2
-#define BUSY 3
-
-static void initExecutionEnvironment(ExecEnvironment *ee)
+static void destroyLockRecord(monitorLockRecord *r)
{
- pthread_mutex_init(&ee->metaLockMutex, NULL);
- pthread_cond_init(&ee->metaLockCond, NULL);
- pthread_mutex_init(&ee->monitorLockMutex, NULL);
- pthread_cond_init(&ee->monitorLockCond, NULL);
-}
-
-static void initLockRecord(monitorLockRecord *r, threadobject *t)
-{
- r->owner = t;
- r->lockCount = 1;
- r->queue = NULL;
+ sem_destroy(&r->queueSem);
+ pthread_mutex_destroy(&r->resolveLock);
+ pthread_cond_destroy(&r->resolveWait);
}
+*/
void initLocks()
{
static void initThreadLocks(threadobject *thread)
{
- int i;
-
- initExecutionEnvironment(&thread->ee);
- for (i=0; i<INITIALLOCKRECORDS; i++) {
- monitorLockRecord *r = &thread->ee.lr[i];
- initLockRecord(r, thread);
- r->nextFree = &thread->ee.lr[i+1];
- }
- thread->ee.lr[i-1].nextFree = NULL;
- thread->ee.firstLR = &thread->ee.lr[0];
-}
-
-static inline int lockState(long r)
-{
- return (int) r & 3;
+ thread->ee.firstLR = NULL;
+ thread->ee.lrpool = NULL;
+ thread->ee.numlr = 0;
}
-static inline void *lockRecord(long r)
-{
- return (void*) (r & ~3L);
-}
-
-static inline long makeLockBits(void *r, long l)
-{
- return ((long) r) | l;
-}
-
-static lockRecordPool *allocLockRecordPool(threadobject *thread, int size)
+static lockRecordPool *allocNewLockRecordPool(threadobject *thread, int size)
{
lockRecordPool *p = mem_alloc(sizeof(lockRecordPoolHeader) + sizeof(monitorLockRecord) * size);
int i;
return p;
}
-static void freeLockRecordPools(lockRecordPool *pool)
+#define INITIALLOCKRECORDS 8
+
+pthread_mutex_t pool_lock;
+lockRecordPool *global_pool;
+
+static void initPools()
{
- while (pool) {
- lockRecordPool *n = pool->header.next;
- mem_free(pool, sizeof(lockRecordPoolHeader) + sizeof(monitorLockRecord) * pool->header.size);
- pool = n;
+ pthread_mutex_init(&pool_lock, NULL);
+}
+
+static lockRecordPool *allocLockRecordPool(threadobject *t, int size)
+{
+ pthread_mutex_lock(&pool_lock);
+ if (global_pool) {
+ int i;
+ lockRecordPool *pool = global_pool;
+ global_pool = pool->header.next;
+ pthread_mutex_unlock(&pool_lock);
+
+ for (i=0; i < pool->header.size; i++)
+ pool->lr[i].ownerThread = t;
+
+ return pool;
}
+ pthread_mutex_unlock(&pool_lock);
+
+ return allocNewLockRecordPool(t, size);
+}
+
+static void freeLockRecordPools(lockRecordPool *pool)
+{
+ lockRecordPoolHeader *last;
+ pthread_mutex_lock(&pool_lock);
+ last = &pool->header;
+ while (last->next)
+ last = &last->next->header;
+ last->next = global_pool;
+ global_pool = pool;
+ pthread_mutex_unlock(&pool_lock);
}
-static monitorLockRecord *allocLockRecord(threadobject *t)
+static monitorLockRecord *allocLockRecordSimple(threadobject *t)
{
monitorLockRecord *r = t->ee.firstLR;
if (!r) {
- int poolsize = t->ee.lrpool ? t->ee.lrpool->header.size * 2 : INITIALLOCKRECORDS * 2;
+ int poolsize = t->ee.numlr ? t->ee.numlr * 2 : INITIALLOCKRECORDS;
lockRecordPool *pool = allocLockRecordPool(t, poolsize);
pool->header.next = t->ee.lrpool;
t->ee.lrpool = pool;
r = &pool->lr[0];
+ t->ee.numlr += pool->header.size;
}
t->ee.firstLR = r->nextFree;
return r;
}
-static void recycleLockRecord(threadobject *t, monitorLockRecord *r)
+static inline void recycleLockRecord(threadobject *t, monitorLockRecord *r)
{
r->nextFree = t->ee.firstLR;
t->ee.firstLR = r;
}
-static monitorLockRecord *appendToQueue(monitorLockRecord *queue, monitorLockRecord *lr)
+void initObjectLock(java_objectheader *o)
{
- monitorLockRecord *queuestart = queue;
- if (!queue)
- return lr;
- while (queue->queue)
- queue = queue->queue;
- queue->queue = lr;
- return queuestart;
+ o->monitorPtr = dummyLR;
}
-static monitorLockRecord *moveMyLRToFront(threadobject *t, monitorLockRecord *lr)
+
+/* get_dummyLR *****************************************************************
+
+ Returns the global dummy monitor lock record. The pointer is
+ required in the code generator to set up a virtual
+ java_objectheader for code patch locking.
+
+*******************************************************************************/
+
+monitorLockRecord *get_dummyLR(void)
{
- monitorLockRecord *pred = NULL;
- monitorLockRecord *firstLR = lr;
- while (lr->owner != t) {
- pred = lr;
- lr = lr->queue;
- }
- if (!pred)
- return lr;
- pred->queue = lr->queue;
- lr->queue = firstLR;
- lr->storedBits = firstLR->storedBits;
- return lr;
+ return dummyLR;
}
-static long getMetaLockSlow(threadobject *t, long predBits);
-static void releaseMetaLockSlow(threadobject *t, long releaseBits);
-static long getMetaLock(threadobject *t, java_objectheader *o)
+static void queueOnLockRecord(monitorLockRecord *lr, java_objectheader *o)
{
- long busyBits = makeLockBits(t, BUSY);
- long lockBits = atomic_swap(&o->monitorBits, busyBits);
- return lockState(lockBits) != BUSY ? lockBits : getMetaLockSlow(t, lockBits);
+ atomic_add(&lr->queuers, 1);
+
+ MEMORY_BARRIER_AFTER_ATOMIC();
+
+ while (lr->o == o)
+ sem_wait(&lr->queueSem);
+
+ atomic_add(&lr->queuers, -1);
}
-static long getMetaLockSlow(threadobject *t, long predBits)
+static void freeLockRecord(monitorLockRecord *lr)
{
- long bits;
- threadobject *pred = lockRecord(predBits);
- pthread_mutex_lock(&pred->ee.metaLockMutex);
- if (!pred->ee.bitsForGrab) {
- pred->ee.succ = t;
- do {
- pthread_cond_wait(&pred->ee.metaLockCond, &pred->ee.metaLockMutex);
- } while (!t->ee.gotMetaLockSlow);
- t->ee.gotMetaLockSlow = false;
- bits = t->ee.metaLockBits;
- } else {
- bits = pred->ee.metaLockBits;
- pred->ee.bitsForGrab = false;
- pthread_cond_signal(&pred->ee.metaLockCond);
- }
- pthread_mutex_unlock(&pred->ee.metaLockMutex);
- return bits;
+ int q;
+ lr->o = NULL;
+ MEMORY_BARRIER();
+ q = lr->queuers;
+ while (q--)
+ sem_post(&lr->queueSem);
}
-static void releaseMetaLock(threadobject *t, java_objectheader *o, long releaseBits)
+static inline void handleWaiter(monitorLockRecord *mlr, monitorLockRecord *lr)
{
- long busyBits = makeLockBits(t, BUSY);
- int locked = compare_and_swap(&o->monitorBits, busyBits, releaseBits) != 0;
-
- if (!locked)
- releaseMetaLockSlow(t, releaseBits);
+ if (lr->waiting)
+ mlr->waiter = lr;
}
-static void releaseMetaLockSlow(threadobject *t, long releaseBits)
+monitorLockRecord *monitorEnter(threadobject *t, java_objectheader *o)
{
- pthread_mutex_lock(&t->ee.metaLockMutex);
- if (t->ee.succ) {
- assert(!t->ee.succ->ee.bitsForGrab);
- assert(!t->ee.bitsForGrab);
- assert(!t->ee.succ->ee.gotMetaLockSlow);
- t->ee.succ->ee.metaLockBits = releaseBits;
- t->ee.succ->ee.gotMetaLockSlow = true;
- t->ee.succ = NULL;
- pthread_cond_signal(&t->ee.metaLockCond);
- } else {
- t->ee.metaLockBits = releaseBits;
- t->ee.bitsForGrab = true;
- do {
- pthread_cond_wait(&t->ee.metaLockCond, &t->ee.metaLockMutex);
- } while (t->ee.bitsForGrab);
+ for (;;) {
+ monitorLockRecord *lr = o->monitorPtr;
+ if (lr->o != o) {
+ monitorLockRecord *nlr, *mlr = allocLockRecordSimple(t);
+ mlr->o = o;
+ if (mlr == lr) {
+ MEMORY_BARRIER();
+ nlr = o->monitorPtr;
+ if (nlr == lr) {
+ handleWaiter(mlr, lr);
+ return mlr;
+ }
+ } else {
+ if (lr->ownerThread != t)
+ mlr->incharge = lr;
+ MEMORY_BARRIER_BEFORE_ATOMIC();
+ nlr = (void*) compare_and_swap((long*) &o->monitorPtr, (long) lr, (long) mlr);
+ }
+ if (nlr == lr) {
+ if (mlr == lr || lr->o != o) {
+ handleWaiter(mlr, lr);
+ return mlr;
+ }
+ while (lr->o == o)
+ queueOnLockRecord(lr, o);
+ handleWaiter(mlr, lr);
+ return mlr;
+ }
+ freeLockRecord(mlr);
+ recycleLockRecord(t, mlr);
+ queueOnLockRecord(nlr, o);
+ } else {
+ if (lr->ownerThread == t) {
+ lr->lockCount++;
+ return lr;
+ }
+ queueOnLockRecord(lr, o);
+ }
}
- pthread_mutex_unlock(&t->ee.metaLockMutex);
}
-static void monitorEnterSlow(threadobject *t, java_objectheader *o, long r);
-
-void monitorEnter(threadobject *t, java_objectheader *o)
+static void wakeWaiters(monitorLockRecord *lr)
{
- long r = getMetaLock(t, o);
- int state = lockState(r);
+ monitorLockRecord *tmplr;
+ s4 q;
- if (state == NEUTRAL) {
- monitorLockRecord *lr = allocLockRecord(t);
- lr->storedBits = r;
- releaseMetaLock(t, o, makeLockBits(lr, LOCKED));
- } else if (state == LOCKED) {
- monitorLockRecord *ownerLR = lockRecord(r);
- if (ownerLR->owner == t) {
- ownerLR->lockCount++;
- releaseMetaLock(t, o, r);
- } else {
- monitorLockRecord *lr = allocLockRecord(t);
- ownerLR->queue = appendToQueue(ownerLR->queue, lr);
- monitorEnterSlow(t, o, r);
- }
- } else if (state == WAITERS) {
- monitorLockRecord *lr = allocLockRecord(t);
- monitorLockRecord *firstWaiterLR = lockRecord(r);
- lr->queue = firstWaiterLR;
- lr->storedBits = firstWaiterLR->storedBits;
- releaseMetaLock(t, o, makeLockBits(lr, LOCKED));
- }
+ /* assign lock record to a temporary variable */
+
+ tmplr = lr;
+
+ do {
+ q = tmplr->queuers;
+
+ while (q--)
+ sem_post(&tmplr->queueSem);
+
+ tmplr = tmplr->waiter;
+ } while (tmplr != NULL && tmplr != lr);
}
-static void monitorEnterSlow(threadobject *t, java_objectheader *o, long r)
+#define GRAB_LR(lr,t) \
+ if (lr->ownerThread != t) { \
+ lr = lr->incharge; \
+ }
+
+#define CHECK_MONITORSTATE(lr,t,mo,a) \
+ if (lr == NULL || lr->o != mo || lr->ownerThread != t) { \
+ *exceptionptr = new_illegalmonitorstateexception(); \
+ a; \
+ }
+
+bool monitorExit(threadobject *t, java_objectheader *o)
{
- monitorLockRecord *lr;
- while (lockState(r) == LOCKED) {
- pthread_mutex_lock(&t->ee.monitorLockMutex);
- releaseMetaLock(t, o, r);
- pthread_cond_wait(&t->ee.monitorLockCond, &t->ee.monitorLockMutex);
- pthread_mutex_unlock(&t->ee.monitorLockMutex);
- r = getMetaLock(t, o);
+ monitorLockRecord *lr = o->monitorPtr;
+ GRAB_LR(lr, t);
+ CHECK_MONITORSTATE(lr, t, o, return false);
+ if (lr->lockCount > 1) {
+ lr->lockCount--;
+ return true;
+ }
+ if (lr->waiter) {
+ monitorLockRecord *wlr = lr->waiter;
+ if (o->monitorPtr != lr ||
+ (void*) compare_and_swap((long*) &o->monitorPtr, (long) lr, (long) wlr) != lr)
+ {
+ monitorLockRecord *nlr = o->monitorPtr;
+ nlr->waiter = wlr;
+ STORE_ORDER_BARRIER();
+ } else
+ wakeWaiters(wlr);
+ lr->waiter = NULL;
}
- assert(lockState(r) == WAITERS);
- lr = moveMyLRToFront(t, lockRecord(r));
- releaseMetaLock(t, o, makeLockBits(lr, LOCKED));
+ freeLockRecord(lr);
+ recycleLockRecord(t, lr);
+ return true;
}
-static void monitorExitSlow(threadobject *t, java_objectheader *o, monitorLockRecord *lr);
-
-void monitorExit(threadobject *t, java_objectheader *o)
+static void removeFromWaiters(monitorLockRecord *lr, monitorLockRecord *wlr)
{
- long r = getMetaLock(t, o);
- monitorLockRecord *ownerLR = lockRecord(r);
- int state = lockState(r);
- if (state == LOCKED && ownerLR->owner == t) {
- assert(ownerLR->lockCount >= 1);
- if (ownerLR->lockCount == 1) {
- if (ownerLR->queue == NULL) {
- assert(lockState(ownerLR->storedBits) == NEUTRAL);
- releaseMetaLock(t, o, ownerLR->storedBits);
- } else {
- ownerLR->queue->storedBits = ownerLR->storedBits;
- monitorExitSlow(t, o, ownerLR->queue);
- ownerLR->queue = NULL;
- }
- recycleLockRecord(t, ownerLR);
- } else {
- ownerLR->lockCount--;
- releaseMetaLock(t, o, r);
+ do {
+ if (lr->waiter == wlr) {
+ lr->waiter = wlr->waiter;
+ break;
}
- } else {
- releaseMetaLock(t, o, r);
- panic("Illegal monitor exception");
- }
+ lr = lr->waiter;
+ } while (lr);
}
-static threadobject *wakeupEE(monitorLockRecord *lr)
+static inline bool timespec_less(const struct timespec *tv1, const struct timespec *tv2)
{
- while (lr->queue && lr->queue->owner->ee.isWaitingForNotify)
- lr = lr->queue;
- return lr->owner;
+ return tv1->tv_sec < tv2->tv_sec || (tv1->tv_sec == tv2->tv_sec && tv1->tv_nsec < tv2->tv_nsec);
}
-static void monitorExitSlow(threadobject *t, java_objectheader *o, monitorLockRecord *lr)
+static bool timeIsEarlier(const struct timespec *tv)
{
- threadobject *wakeEE = wakeupEE(lr);
- if (wakeEE) {
- pthread_mutex_lock(&wakeEE->ee.monitorLockMutex);
- releaseMetaLock(t, o, makeLockBits(lr, WAITERS));
- pthread_cond_signal(&wakeEE->ee.monitorLockCond);
- pthread_mutex_unlock(&wakeEE->ee.monitorLockMutex);
- } else {
- releaseMetaLock(t, o, makeLockBits(lr, WAITERS));
- }
+ struct timeval tvnow;
+ struct timespec tsnow;
+ gettimeofday(&tvnow, NULL);
+ tsnow.tv_sec = tvnow.tv_sec;
+ tsnow.tv_nsec = tvnow.tv_usec * 1000;
+ return timespec_less(&tsnow, tv);
}
-void monitorWait(threadobject *t, java_objectheader *o, s8 millis)
-{
- long r = getMetaLock(t, o);
- monitorLockRecord *ownerLR = lockRecord(r);
- int state = lockState(r);
- if (state == LOCKED && ownerLR->owner == t) {
- pthread_mutex_lock(&t->ee.monitorLockMutex);
- t->ee.isWaitingForNotify = true;
- monitorExitSlow(t, o, ownerLR);
- if (millis == -1)
- pthread_cond_wait(&t->ee.monitorLockCond, &t->ee.monitorLockMutex);
- else
- timedCondWait(&t->ee.monitorLockCond, &t->ee.monitorLockMutex, millis);
- t->ee.isWaitingForNotify = false;
- pthread_mutex_unlock(&t->ee.monitorLockMutex);
- r = getMetaLock(t, o);
- monitorEnterSlow(t, o, r);
+
+/* waitWithTimeout *************************************************************
+
+ XXX
+
+*******************************************************************************/
+
+static bool waitWithTimeout(threadobject *t, monitorLockRecord *lr, struct timespec *wakeupTime)
+{
+ bool wasinterrupted;
+
+ pthread_mutex_lock(&t->waitLock);
+
+ t->isSleeping = true;
+
+ if (wakeupTime->tv_sec || wakeupTime->tv_nsec)
+ while (!t->interrupted && !t->signaled && timeIsEarlier(wakeupTime))
+ pthread_cond_timedwait(&t->waitCond, &t->waitLock, wakeupTime);
+ else
+ while (!t->interrupted && !t->signaled)
+ pthread_cond_wait(&t->waitCond, &t->waitLock);
+
+ wasinterrupted = t->interrupted;
+ t->interrupted = false;
+ t->signaled = false;
+ t->isSleeping = false;
+
+ pthread_mutex_unlock(&t->waitLock);
+
+ return wasinterrupted;
+}
+
+
+static void calcAbsoluteTime(struct timespec *tm, s8 millis, s4 nanos)
+{
+ if (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 {
- releaseMetaLock(t, o, r);
- panic("Illegal monitor exception");
+ tm->tv_sec = 0;
+ tm->tv_nsec = 0;
}
}
+void monitorWait(threadobject *t, java_objectheader *o, s8 millis, s4 nanos)
+{
+ bool wasinterrupted;
+ struct timespec wakeupTime;
+ monitorLockRecord *mlr, *lr = o->monitorPtr;
+ GRAB_LR(lr, t);
+ CHECK_MONITORSTATE(lr, t, o, return);
+
+ calcAbsoluteTime(&wakeupTime, millis, nanos);
+
+ if (lr->waiter)
+ wakeWaiters(lr->waiter);
+ lr->waiting = true;
+ STORE_ORDER_BARRIER();
+ freeLockRecord(lr);
+ wasinterrupted = waitWithTimeout(t, lr, &wakeupTime);
+ mlr = monitorEnter(t, o);
+ removeFromWaiters(mlr, lr);
+ mlr->lockCount = lr->lockCount;
+ lr->lockCount = 1;
+ lr->waiting = false;
+ lr->waiter = NULL;
+ recycleLockRecord(t, lr);
+
+ if (wasinterrupted)
+ *exceptionptr = new_exception(string_java_lang_InterruptedException);
+}
+
static void notifyOneOrAll(threadobject *t, java_objectheader *o, bool one)
{
- long r = getMetaLock(t, o);
- monitorLockRecord *ownerLR = lockRecord(r);
- int state = lockState(r);
- if (state == LOCKED && ownerLR->owner == t) {
- monitorLockRecord *q = ownerLR->queue;
- while (q) {
- if (q->owner->ee.isWaitingForNotify) {
- q->owner->ee.isWaitingForNotify = false;
- if (one)
- break;
- }
- q = q->queue;
- }
- releaseMetaLock(t, o, r);
- } else {
- releaseMetaLock(t, o, r);
- panic("Illegal monitor exception");
- }
+ monitorLockRecord *lr = o->monitorPtr;
+ GRAB_LR(lr, t);
+ CHECK_MONITORSTATE(lr, t, o, return);
+ do {
+ threadobject *wthread;
+ monitorLockRecord *wlr = lr->waiter;
+ if (!wlr)
+ break;
+ wthread = wlr->ownerThread;
+ pthread_mutex_lock(&wthread->waitLock);
+ if (wthread->isSleeping)
+ pthread_cond_signal(&wthread->waitCond);
+ wthread->signaled = true;
+ pthread_mutex_unlock(&wthread->waitLock);
+ lr = wlr;
+ } while (!one);
+}
+
+bool threadHoldsLock(threadobject *t, java_objectheader *o)
+{
+ monitorLockRecord *lr = o->monitorPtr;
+ GRAB_LR(lr, t);
+ /* The reason why we have to check against NULL is that
+ * dummyLR->incharge == NULL */
+ return lr && lr->o == o && lr->ownerThread == t;
+}
+
+void interruptThread(java_lang_VMThread *thread)
+{
+ threadobject *t = (threadobject*) thread;
+
+ t->interrupted = true;
+ pthread_mutex_lock(&t->waitLock);
+ if (t->isSleeping)
+ pthread_cond_signal(&t->waitCond);
+ pthread_mutex_unlock(&t->waitLock);
+}
+
+bool interruptedThread()
+{
+ threadobject *t = (threadobject*) THREADOBJECT;
+ bool intr = t->interrupted;
+ t->interrupted = false;
+ return intr;
+}
+
+bool isInterruptedThread(java_lang_VMThread *thread)
+{
+ threadobject *t = (threadobject*) thread;
+ return t->interrupted;
}
-void wait_cond_for_object(java_objectheader *o, s8 time)
+void sleepThread(s8 millis, s4 nanos)
{
+ bool wasinterrupted;
threadobject *t = (threadobject*) THREADOBJECT;
- monitorWait(t, o, time);
+ monitorLockRecord *lr;
+ struct timespec wakeupTime;
+ calcAbsoluteTime(&wakeupTime, millis, nanos);
+
+ lr = allocLockRecordSimple(t);
+ wasinterrupted = waitWithTimeout(t, lr, &wakeupTime);
+ recycleLockRecord(t, lr);
+
+ if (wasinterrupted)
+ *exceptionptr = new_exception(string_java_lang_InterruptedException);
+}
+
+void yieldThread()
+{
+ sched_yield();
+}
+
+void setPriorityThread(thread *t, s4 priority)
+{
+ nativethread *info = &((threadobject*) t->vmThread)->info;
+ setPriority(info->tid, priority);
+}
+
+void wait_cond_for_object(java_objectheader *o, s8 time, s4 nanos)
+{
+ threadobject *t = (threadobject*) THREADOBJECT;
+ monitorWait(t, o, time, nanos);
}
void signal_cond_for_object(java_objectheader *o)
notifyOneOrAll(t, o, false);
}
+
+/* threads_dump ****************************************************************
+
+ Dumps info for all threads running in the JVM. This function is
+ called when SIGQUIT (<ctrl>-\) is sent to CACAO.
+
+*******************************************************************************/
+
+void threads_dump(void)
+{
+ threadobject *tobj;
+ java_lang_VMThread *vmt;
+ nativethread *nt;
+ ExecEnvironment *ee;
+ java_lang_Thread *t;
+ utf *name;
+
+ tobj = mainthreadobj;
+
+ printf("Full thread dump CACAO "VERSION":\n");
+
+ /* iterate over all started threads */
+
+ do {
+ /* get thread objects */
+
+ vmt = &tobj->o;
+ nt = &tobj->info;
+ ee = &tobj->ee;
+ t = vmt->thread;
+
+ /* the thread may be currently in initalization, don't print it */
+
+ if (t) {
+ /* get thread name */
+
+ name = javastring_toutf(t->name, false);
+
+ printf("\n\"");
+ utf_display(name);
+ printf("\" ");
+
+ if (t->daemon)
+ printf("daemon ");
+
+#if SIZEOF_VOID_P == 8
+ printf("prio=%d tid=0x%016lx\n", t->priority, nt->tid);
+#else
+ printf("prio=%d tid=0x%08lx\n", t->priority, nt->tid);
#endif
+ /* send SIGUSR1 to thread to print stacktrace */
+
+ pthread_kill(nt->tid, SIGUSR1);
+
+ /* sleep this thread a bit, so the signal can reach the thread */
+
+ sleepThread(10, 0);
+ }
+
+ tobj = tobj->info.next;
+ } while (tobj && (tobj != mainthreadobj));
+}
+
/*
* These are local overrides for various environment variables in Emacs.