/* 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
+ Copyright (C) 1996-2005, 2006 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.
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.
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
- Contact: cacao@complang.tuwien.ac.at
+ Contact: cacao@cacaojvm.org
Authors: Stefan Ring
Changes: Christian Thalinger
+ Edwin Steiner
- $Id: threads.c 2517 2005-05-23 10:33:06Z twisti $
+ $Id: threads.c 4871 2006-05-03 23:04:30Z edwin $
*/
+#include "config.h"
+
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <errno.h>
-#include "codegen.h"
-#include "config.h"
+#include <pthread.h>
+#include <semaphore.h>
+
+#include "vm/types.h"
+
+#include "arch.h"
+
+#ifndef USE_MD_THREAD_STUFF
+#include "machine-instr.h"
+#else
+#include "threads/native/generic-primitives.h"
+#endif
+
#include "mm/boehm.h"
#include "mm/memory.h"
#include "native/native.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/vm.h"
#include "vm/jit/asmpart.h"
-#include <pthread.h>
-#include <semaphore.h>
-
#if !defined(__DARWIN__)
#if defined(__LINUX__)
#define GC_LINUX_THREADS
#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
+
+/******************************************************************************/
+/* Recursive Mutex Implementation for Darwin */
+/******************************************************************************/
+
#ifdef MUTEXSIM
/* We need this for older MacOSX (10.1.x) */
static void pthread_mutex_lock_rec(pthread_mutex_rec_t *m)
{
- for (;;)
+ for (;;) {
if (!m->count)
{
pthread_mutex_lock(&m->mutex);
m->owner = pthread_self();
m->count++;
break;
- } else {
- if (m->owner != pthread_self())
+ }
+ else {
+ if (m->owner != pthread_self()) {
pthread_mutex_lock(&m->mutex);
- else
- {
+ }
+ else {
m->count++;
break;
}
}
+ }
}
static void pthread_mutex_unlock_rec(pthread_mutex_rec_t *m)
#endif /* MUTEXSIM */
-static void setPriority(pthread_t tid, int priority)
+/* 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);
+
+ fprintf(stderr,"error: sem_init returned unexpected error %d: %s\n",
+ errno, strerror(errno));
+ abort();
+}
+
+/* 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);
+
+ fprintf(stderr,"error: sem_wait returned unexpected error %d: %s\n",
+ errno, strerror(errno));
+ abort();
+}
+
+/* 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;
+
+ fprintf(stderr,"error: sem_post returned unexpected error %d: %s\n",
+ errno, strerror(errno));
+ abort();
+}
+
+/* threads_set_thread_priority *************************************************
+
+ Set the priority of the given thread.
+
+ IN:
+ tid..........thread id
+ priority.....priority to set
+
+******************************************************************************/
+
+static void threads_set_thread_priority(pthread_t tid, int priority)
{
struct sched_param schedp;
int policy;
pthread_setschedparam(tid, policy, &schedp);
}
-#include "machine-instr.h"
-static struct avl_table *criticaltree;
-static threadobject *mainthreadobj;
+static avl_tree *criticaltree;
+threadobject *mainthreadobj;
#ifndef HAVE___THREAD
pthread_key_t tkey_threadinfo;
#endif
static pthread_mutex_rec_t compiler_mutex;
-static pthread_mutex_rec_t tablelock;
void compiler_lock()
{
pthread_mutex_unlock_rec(&compiler_mutex);
}
-void tables_lock()
-{
- pthread_mutex_lock_rec(&tablelock);
-}
-
-void tables_unlock()
-{
- pthread_mutex_unlock_rec(&tablelock);
-}
-
-static int criticalcompare(const void *pa, const void *pb, void *param)
+static s4 criticalcompare(const void *pa, const void *pb)
{
const threadcritnode *na = pa;
const threadcritnode *nb = pb;
return 0;
}
+
static const threadcritnode *findcritical(u1 *mcodeptr)
{
- struct avl_node *n = criticaltree->avl_root;
- const threadcritnode *m = NULL;
+ avl_node *n;
+ const threadcritnode *m;
+
+ n = criticaltree->root;
+ m = NULL;
+
if (!n)
return NULL;
- for (;;)
- {
- const threadcritnode *d = n->avl_data;
+
+ for (;;) {
+ const threadcritnode *d = n->data;
+
if (mcodeptr == d->mcodebegin)
return d;
+
if (mcodeptr < d->mcodebegin) {
- if (n->avl_link[0])
- n = n->avl_link[0];
- else
+ if (n->childs[0]) {
+ n = n->childs[0];
+ }
+ else {
return m;
- } else {
- if (n->avl_link[1]) {
- m = n->avl_data;
- n = n->avl_link[1];
- } else
- return n->avl_data;
+ }
+ }
+ else {
+ if (n->childs[1]) {
+ m = n->data;
+ n = n->childs[1];
+ }
+ else {
+ return n->data;
+ }
}
}
}
+
void thread_registercritical(threadcritnode *n)
{
avl_insert(criticaltree, n);
static void thread_addstaticcritical()
{
+ /* XXX TWISTI: this is just a quick hack */
+#if defined(ENABLE_JIT)
threadcritnode *n = &asm_criticalsections;
while (n->mcodebegin)
thread_registercritical(n++);
+#endif
}
static pthread_mutex_t threadlistlock;
threadobject *tobj = mainthreadobj;
nativethread *infoself = THREADINFO;
- if (count == 0)
+ if (count == 0) {
do {
count++;
tobj = tobj->info.next;
} while (tobj != mainthreadobj);
+ }
do {
nativethread *info = &tobj->info;
#else
count = cast_sendsignals(GC_signum1(), 0);
for (i=0; i<count; i++)
- sem_wait(&suspend_ack);
+ threads_sem_wait(&suspend_ack);
#endif
pthread_mutex_unlock(&threadlistlock);
}
{
int sig;
sigset_t sigs;
-
+
+ /* XXX TWISTI: this is just a quick hack */
+#if defined(ENABLE_JIT)
thread_restartcriticalsection(ctx);
+#endif
/* 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);
+ threads_sem_post(&suspend_ack);
pthread_cond_wait(&suspend_cond, &suspend_ack_lock);
pthread_mutex_unlock(&suspend_ack_lock);
#else
- sem_post(&suspend_ack);
+ threads_sem_post(&suspend_ack);
sig = GC_signum2();
sigfillset(&sigs);
}
#endif
+#if !defined(ENABLE_JVMTI)
static void setthreadobject(threadobject *thread)
+#else
+void setthreadobject(threadobject *thread)
+#endif
{
#if !defined(HAVE___THREAD)
pthread_setspecific(tkey_threadinfo, thread);
#endif
}
+
+/* thread_setself **************************************************************
+
+ Return the threadobject of the current thread.
+
+ XXX The return value type should be changed to a typed pointer.
+
+*******************************************************************************/
+
+void *thread_getself(void)
+{
+ return THREADOBJECT;
+}
+
+/* unlocked dummy record - avoids NULL checks */
static monitorLockRecord *dummyLR;
static void initPools();
-/*
- * Initialize threads.
- */
-void
-initThreadsEarly()
+
+/* thread_preinit **************************************************************
+
+ Do some early initialization of stuff required.
+
+*******************************************************************************/
+
+void threads_preinit(void)
{
#ifndef MUTEXSIM
pthread_mutexattr_t mutexattr;
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&compiler_mutex, &mutexattr);
- pthread_mutex_init(&tablelock, &mutexattr);
pthread_mutexattr_destroy(&mutexattr);
#else
pthread_mutex_init_rec(&compiler_mutex);
- pthread_mutex_init_rec(&tablelock);
#endif
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);
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 */
- /* 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;
+ dummyLR->waiting = NULL;
+ dummyLR->incharge = dummyLR;
+
+ /* we need a working dummyLR before initializing the critical
+ section tree */
+
+ criticaltree = avl_create(&criticalcompare);
+
+ thread_addstaticcritical();
+ threads_sem_init(&suspend_ack, 0, 0);
}
+
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;
- java_lang_String *threadname;
- java_lang_Thread *mainthread;
+ java_lang_String *threadname;
+ java_lang_Thread *mainthread;
java_lang_ThreadGroup *threadgroup;
- threadobject *tempthread = mainthreadobj;
- methodinfo *method;
+ threadobject *tempthread;
+ methodinfo *method;
- if (!load_class_bootstrap(utf_new_char("java/lang/VMThread"),&threadclass))
- throw_exception_exit();
- if (!threadclass)
- throw_exception_exit();
- if (!link_class(threadclass))
- throw_exception_exit();
+ tempthread = mainthreadobj;
freeLockRecordPools(mainthreadobj->ee.lrpool);
- /* 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 */
- threadclass->instancesize = sizeof(threadobject);
- /* Create a VMThread */
- mainthreadobj = (threadobject *) builtin_new(threadclass);
+ /* 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)
- throw_exception_exit();
+ return false;
FREE(tempthread, threadobject);
+
initThread(&mainthreadobj->o);
setthreadobject(mainthreadobj);
initLocks();
+
mainthreadobj->info.next = mainthreadobj;
mainthreadobj->info.prev = mainthreadobj;
+#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 */
+ /* allocate and init ThreadGroup */
threadgroup = (java_lang_ThreadGroup *)
native_new_and_init(class_java_lang_ThreadGroup);
if (!threadgroup)
throw_exception_exit();
- /* Create a Thread */
- if (!load_class_bootstrap(utf_new_char("java/lang/Thread"),&threadclass))
- throw_exception_exit();
- mainthread = (java_lang_Thread*) builtin_new(threadclass);
- mainthreadobj->o.thread = mainthread;
+ /* create a Thread */
+
+ mainthread = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
if (!mainthread)
throw_exception_exit();
- /* Call Thread constructor */
- method = class_resolveclassmethod(threadclass,
+ 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"),
- threadclass,
+ class_java_lang_Thread,
true);
if (!method)
- throw_exception_exit();
+ return false;
+
+ (void) vm_call_method(method, (java_objectheader *) mainthread,
+ mainthreadobj, threadname, 5, false);
- asm_calljavafunction(method, mainthread, mainthreadobj, threadname, (void*) 5);
if (*exceptionptr)
- throw_exception_exit();
+ return false;
mainthread->group = threadgroup;
- /* XXX This is a hack because the fourth argument was omitted */
- mainthread->daemon = false;
- /* Add mainthread to ThreadGroup */
+ /* add mainthread to ThreadGroup */
+
method = class_resolveclassmethod(class_java_lang_ThreadGroup,
utf_new_char("addThread"),
utf_new_char("(Ljava/lang/Thread;)V"),
true);
if (!method)
- throw_exception_exit();
+ return false;
+
+ (void) vm_call_method(method, (java_objectheader *) threadgroup,
+ mainthread);
- asm_calljavafunction(method, threadgroup, mainthread, NULL, NULL);
if (*exceptionptr)
- throw_exception_exit();
+ return false;
- setPriority(pthread_self(), 5);
+ threads_set_thread_priority(pthread_self(), 5);
pthread_attr_init(&threadattr);
pthread_attr_setdetachstate(&threadattr, PTHREAD_CREATE_DETACHED);
+
+ /* everything's ok */
+
+ return true;
}
+
void initThread(java_lang_VMThread *t)
{
threadobject *thread = (threadobject*) t;
static void initThreadLocks(threadobject *);
+
+/* startupinfo *****************************************************************
+
+ Struct used to pass info from threads_start_thread to
+ threads_startup_thread.
+
+******************************************************************************/
+
typedef struct {
- threadobject *thread;
- sem_t *psem;
+ 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;
-static void *threadstartup(void *t)
+
+/* threads_startup_thread ******************************************************
+
+ Thread startup function called by pthread_create.
+
+ 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:
+ t............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 *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);
+ } else {
+ intrp_thread_stack = NULL;
+ }
+#endif
+
+ /* get passed startupinfo structure and the values in there */
+
+ startup = t;
+ t = NULL; /* make sure it's not used wrongly */
+
+ 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. */
+ threads_sem_wait(startup->psem_first);
+
+ /* set the thread object */
- t = NULL;
#if defined(__DARWIN__)
info->mach_thread = mach_thread_self();
#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);
+ /* init data structures of this thread */
+
initThreadLocks(thread);
+ /* tell threads_startup_thread that we registered ourselves */
+ /* CAUTION: *startup becomes invalid with this! */
+
startup = NULL;
- sem_post(psem);
+ threads_sem_post(psem);
- setPriority(info->tid, thread->o.thread->priority);
- sched_yield();
+ /* set our priority */
- /* Find the run()V method and call it */
- method = class_resolveclassmethod(thread->o.header.vftbl->class,
- utf_new_char("run"),
- utf_new_char("()V"),
- thread->o.header.vftbl->class,
- true);
+ threads_set_thread_priority(info->tid, thread->o.thread->priority);
- /* if method != NULL, we had not exception */
- if (method) {
- asm_calljavafunction(method, thread, NULL, NULL, NULL);
+#if defined(ENABLE_INTRP)
+ /* set interpreter stack */
- } else {
- throw_exception();
+ 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();
+
+ (void) vm_call_method(method, (java_objectheader *) thread);
+
+ }
+ 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. */
+ /* 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);
+
+ /* tell everyone that a thread has finished */
+
pthread_cond_broadcast(&info->joinCond);
return NULL;
}
-void startThread(thread *t)
+
+/* threads_start_thread ********************************************************
+
+ Start a thread in the JVM.
+
+ IN:
+ t............the java.lang.Thread object
+ function.....function to run in the new thread. NULL means that the
+ "run" method of the object `t` should be called
+
+******************************************************************************/
+
+void threads_start_thread(thread *t, functionptr function)
{
- nativethread *info = &((threadobject*) t->vmThread)->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
+ * threads_startup_thread */
- startup.thread = (threadobject*) t->vmThread;
- 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);
-
- if (pthread_create(&info->tid, &threadattr, threadstartup, &startup)) {
+ threads_sem_init(&sem, 0, 0);
+ threads_sem_init(&sem_first, 0, 0);
+
+ /* create the thread */
+
+ if (pthread_create(&info->tid, &threadattr, threads_startup_thread,
+ &startup)) {
log_text("pthread_create failed");
assert(0);
}
- /* Wait here until the thread has entered itself into the thread list */
- sem_wait(&sem);
+ /* signal that pthread_create has returned, so info->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);
}
+
/* At the end of the program, we wait for all running non-daemon threads to die
*/
r->o = NULL;
r->waiter = NULL;
r->incharge = (monitorLockRecord *) &dummyLR;
- r->waiting = false;
- sem_init(&r->queueSem, 0, 0);
+ r->waiting = NULL;
+ threads_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.
-
-static void destroyLockRecord(monitorLockRecord *r)
-{
- sem_destroy(&r->queueSem);
- pthread_mutex_destroy(&r->resolveLock);
- pthread_cond_destroy(&r->resolveWait);
-}
-*/
-
void initLocks()
{
initThreadLocks(mainthreadobj);
#define INITIALLOCKRECORDS 8
-static pthread_mutex_t pool_lock;
-static lockRecordPool *global_pool;
+pthread_mutex_t pool_lock;
+lockRecordPool *global_pool;
static void initPools()
{
global_pool = pool->header.next;
pthread_mutex_unlock(&pool_lock);
- for (i=0; i < pool->header.size; i++)
+ for (i=0; i < pool->header.size; i++) {
pool->lr[i].ownerThread = t;
-
+ pool->lr[i].nextFree = &pool->lr[i+1];
+ }
+ pool->lr[i-1].nextFree = NULL;
+
return pool;
}
pthread_mutex_unlock(&pool_lock);
static monitorLockRecord *allocLockRecordSimple(threadobject *t)
{
- monitorLockRecord *r = t->ee.firstLR;
+ monitorLockRecord *r;
+
+ assert(t);
+ r = t->ee.firstLR;
if (!r) {
int poolsize = t->ee.numlr ? t->ee.numlr * 2 : INITIALLOCKRECORDS;
r = &pool->lr[0];
t->ee.numlr += pool->header.size;
}
-
+
t->ee.firstLR = r->nextFree;
+#ifndef NDEBUG
+ r->nextFree = NULL; /* in order to find invalid uses of nextFree */
+#endif
return r;
}
static inline void recycleLockRecord(threadobject *t, monitorLockRecord *r)
{
+ assert(t);
+ assert(r);
+ assert(r->ownerThread == t);
+ assert(r->nextFree == NULL);
+
r->nextFree = t->ee.firstLR;
t->ee.firstLR = r;
}
+/* initObjectLock **************************************************************
+
+ Initialize the monitor pointer of the given object. The monitor gets
+ initialized to an unlocked state.
+
+*******************************************************************************/
+
void initObjectLock(java_objectheader *o)
{
+ assert(o);
+
o->monitorPtr = dummyLR;
}
{
atomic_add(&lr->queuers, 1);
MEMORY_BARRIER_AFTER_ATOMIC();
- while (lr->o == o)
- sem_wait(&lr->queueSem);
+
+ if (lr->o == o)
+ threads_sem_wait(&lr->queueSem);
+
atomic_add(&lr->queuers, -1);
}
MEMORY_BARRIER();
q = lr->queuers;
while (q--)
- sem_post(&lr->queueSem);
+ threads_sem_post(&lr->queueSem);
}
-static inline void handleWaiter(monitorLockRecord *mlr, monitorLockRecord *lr)
+static inline void handleWaiter(monitorLockRecord *newlr,
+ monitorLockRecord *curlr,
+ java_objectheader *o)
{
- if (lr->waiting)
- mlr->waiter = lr;
+ /* if the current lock record is used for waiting on the object */
+ /* `o`, then record it as a waiter in the new lock record */
+
+ if (curlr->waiting == o)
+ newlr->waiter = curlr;
}
+/* monitorEnter ****************************************************************
+
+ Acquire the monitor of the given object. If the current thread already
+ owns the monitor, the lock counter is simply increased.
+
+ This function blocks until it can acquire the monitor.
+
+ IN:
+ t............the current thread
+ o............the object of which to enter the monitor
+
+ RETURN VALUE:
+ the new lock record of the object when it has been entered
+
+*******************************************************************************/
+
monitorLockRecord *monitorEnter(threadobject *t, java_objectheader *o)
{
for (;;) {
monitorLockRecord *lr = o->monitorPtr;
if (lr->o != o) {
- monitorLockRecord *nlr, *mlr = allocLockRecordSimple(t);
+ /* the lock record does not lock this object */
+ monitorLockRecord *nlr;
+ monitorLockRecord *mlr;
+
+ /* allocate a new lock record for this object */
+ mlr = allocLockRecordSimple(t);
mlr->o = o;
+
+ /* check if it is the same record the object refered to earlier */
if (mlr == lr) {
MEMORY_BARRIER();
nlr = o->monitorPtr;
if (nlr == lr) {
- handleWaiter(mlr, lr);
+ /* the object still refers to the same lock record */
+ /* got it! */
+ handleWaiter(mlr, lr, o);
return mlr;
}
- } else {
+ }
+ else {
+ /* no, it's another lock record */
+ /* if we don't own the old record, set incharge XXX */
if (lr->ownerThread != t)
mlr->incharge = lr;
+
+ /* if the object still refers to lr, replace it by the new mlr */
MEMORY_BARRIER_BEFORE_ATOMIC();
- nlr = (void*) compare_and_swap((long*) &o->monitorPtr, (long) lr, (long) mlr);
+ nlr = (monitorLockRecord *) compare_and_swap((long*) &o->monitorPtr, (long) lr, (long) mlr);
}
+
if (nlr == lr) {
+ /* we swapped the new record in successfully */
if (mlr == lr || lr->o != o) {
- handleWaiter(mlr, lr);
+ /* the old lock record is the same as the new one, or */
+ /* it locks another object. */
+ /* got it! */
+ handleWaiter(mlr, lr, o);
return mlr;
}
+ /* lr locks the object, we have to wait */
while (lr->o == o)
queueOnLockRecord(lr, o);
- handleWaiter(mlr, lr);
+
+ /* got it! */
+ handleWaiter(mlr, lr, o);
return mlr;
}
+
+ /* forget this mlr lock record, wait on nlr and try again */
freeLockRecord(mlr);
recycleLockRecord(t, mlr);
queueOnLockRecord(nlr, o);
- } else {
+ }
+ else {
+ /* the lock record is for the object we want */
+
if (lr->ownerThread == t) {
+ /* we own it already, just recurse */
lr->lockCount++;
return lr;
}
+
+ /* it's locked. we wait and then try again */
queueOnLockRecord(lr, o);
}
}
}
-static void wakeWaiters(monitorLockRecord *lr)
+/* threads_wake_waiters ********************************************************
+
+ For each lock record in the given waiter list, post the queueSem
+ once for each queuer of the lock record.
+
+ IN:
+ lr...........the head of the waiter list
+
+*******************************************************************************/
+
+static void threads_wake_waiters(monitorLockRecord *lr)
{
+ monitorLockRecord *tmplr;
+ s4 q;
+
+ /* move it to a local variable (Stefan commented this especially.
+ * Might be important somehow...) */
+
+ tmplr = lr;
+
do {
- int q = lr->queuers;
+ q = tmplr->queuers;
+
while (q--)
- sem_post(&lr->queueSem);
- lr = lr->waiter;
- } while (lr);
+ threads_sem_post(&tmplr->queueSem);
+
+ tmplr = tmplr->waiter;
+ } while (tmplr != NULL && tmplr != lr); /* this breaks cycles to lr */
}
#define GRAB_LR(lr,t) \
#define CHECK_MONITORSTATE(lr,t,mo,a) \
if (lr->o != mo || lr->ownerThread != t) { \
- *exceptionptr = new_exception(string_java_lang_IllegalMonitorStateException); \
+ *exceptionptr = new_illegalmonitorstateexception(); \
a; \
}
+/* monitorExit *****************************************************************
+
+ Decrement the counter of a (currently owned) monitor. If the counter
+ reaches zero, release the monitor.
+
+ If the current thread is not the owner of the monitor, an
+ IllegalMonitorState exception is thrown.
+
+ IN:
+ t............the current thread
+ o............the object of which to exit the monitor
+
+ RETURN VALUE:
+ true.........everything ok,
+ false........an exception has been thrown
+
+*******************************************************************************/
+
bool monitorExit(threadobject *t, java_objectheader *o)
{
- monitorLockRecord *lr = o->monitorPtr;
+ monitorLockRecord *lr;
+
+ lr = o->monitorPtr;
GRAB_LR(lr, t);
CHECK_MONITORSTATE(lr, t, o, return false);
+
+ /* { the current thread `t` owns the lock record `lr` on object `o` } */
+
if (lr->lockCount > 1) {
+ /* we had locked this one recursively. just decrement, it will */
+ /* still be locked. */
lr->lockCount--;
return true;
}
+
+ /* we are going to unlock and recycle this lock record */
+
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;
+ nlr->waiter = wlr; /* XXX is it ok to overwrite the nlr->waiter field like that? */
STORE_ORDER_BARRIER();
- } else
- wakeWaiters(wlr);
+ }
+ else {
+ threads_wake_waiters(wlr);
+ }
lr->waiter = NULL;
}
+
+ /* unlock and throw away this lock record */
freeLockRecord(lr);
recycleLockRecord(t, lr);
return true;
}
-static void removeFromWaiters(monitorLockRecord *lr, monitorLockRecord *wlr)
+/* threads_remove_waiter *******************************************************
+
+ Remove a waiter lock record from the waiter list of the given lock record
+
+ IN:
+ lr...........the lock record holding the waiter list
+ toremove.....the record to remove from the list
+
+*******************************************************************************/
+
+static void threads_remove_waiter(monitorLockRecord *lr,
+ monitorLockRecord *toremove)
{
do {
- if (lr->waiter == wlr) {
- lr->waiter = wlr->waiter;
+ if (lr->waiter == toremove) {
+ lr->waiter = toremove->waiter;
break;
}
lr = lr->waiter;
- } while (lr);
+ } while (lr); /* XXX need to break cycle? */
}
-static inline bool timespec_less(const struct timespec *tv1, const struct timespec *tv2)
+/* 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);
+ return (tv1->tv_sec < tv2->tv_sec)
+ ||
+ (tv1->tv_sec == tv2->tv_sec && tv1->tv_nsec < tv2->tv_nsec);
}
-static bool timeIsEarlier(const struct timespec *tv)
+/* 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;
- gettimeofday(&tvnow, NULL);
+
+ /* get current time */
+
+ if (gettimeofday(&tvnow, NULL) != 0) {
+ fprintf(stderr,"error: gettimeofday returned unexpected error %d: %s\n",
+ errno, strerror(errno));
+ abort();
+ }
+
+ /* convert it to a timespec */
+
tsnow.tv_sec = tvnow.tv_sec;
tsnow.tv_nsec = tvnow.tv_usec * 1000;
- return timespec_less(&tsnow, tv);
+
+ /* compare current time with the given timespec */
+
+ return threads_timespec_earlier(&tsnow, tv);
}
-bool waitWithTimeout(threadobject *t, monitorLockRecord *lr, struct timespec *wakeupTime)
+
+/* threads_wait_with_timeout ***************************************************
+
+ 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
+ wakeupTime...absolute (latest) wakeup time
+ If both tv_sec and tv_nsec are zero, this function
+ waits for an unlimited amount of time.
+
+ RETURN VALUE:
+ true.........if the wait has been interrupted,
+ false........if the wait was ended by notification or timeout
+
+*******************************************************************************/
+
+static bool threads_wait_with_timeout(threadobject *t,
+ struct timespec *wakeupTime)
{
bool wasinterrupted;
+ /* acquire the waitLock */
+
pthread_mutex_lock(&t->waitLock);
+
+ /* mark us as sleeping */
+
t->isSleeping = true;
- if (wakeupTime->tv_sec || wakeupTime->tv_nsec)
- while (!t->interrupted && !t->signaled && timeIsEarlier(wakeupTime))
+
+ /* wait on waitCond */
+
+ if (wakeupTime->tv_sec || wakeupTime->tv_nsec) {
+ /* with timeout */
+ while (!t->interrupted && !t->signaled
+ && threads_current_time_is_earlier_than(wakeupTime))
+ {
pthread_cond_timedwait(&t->waitCond, &t->waitLock, wakeupTime);
- else
+ }
+ }
+ else {
+ /* no timeout */
while (!t->interrupted && !t->signaled)
pthread_cond_wait(&t->waitCond, &t->waitLock);
+ }
+
+ /* check if we were interrupted */
+
wasinterrupted = t->interrupted;
+
+ /* reset all flags */
+
t->interrupted = false;
t->signaled = false;
t->isSleeping = false;
+
+ /* release the waitLock */
+
pthread_mutex_unlock(&t->waitLock);
+
return wasinterrupted;
}
+
static void calcAbsoluteTime(struct timespec *tm, s8 millis, s4 nanos)
{
if (millis || nanos) {
nsec = tv.tv_usec * 1000 + (s4) millis * 1000000 + nanos;
tm->tv_sec = tv.tv_sec + nsec / 1000000000;
tm->tv_nsec = nsec % 1000000000;
- } else {
+ }
+ else {
tm->tv_sec = 0;
tm->tv_nsec = 0;
}
}
+/* monitorWait *****************************************************************
+
+ Wait on an object for a given (maximum) amount of time.
+
+ IN:
+ t............the current thread
+ o............the object
+ millis.......milliseconds of timeout
+ nanos........nanoseconds of timeout
+
+ PRE-CONDITION:
+ The current thread must be the owner of the object's monitor.
+
+*******************************************************************************/
+
void monitorWait(threadobject *t, java_objectheader *o, s8 millis, s4 nanos)
{
bool wasinterrupted;
struct timespec wakeupTime;
- monitorLockRecord *mlr, *lr = o->monitorPtr;
+ monitorLockRecord *newlr;
+ monitorLockRecord *lr;
+
+ lr = o->monitorPtr;
GRAB_LR(lr, t);
CHECK_MONITORSTATE(lr, t, o, return);
+ /* { the thread t owns the lock record lr on the object o } */
+
+ /* calculate the the (latest) wakeup time */
+
calcAbsoluteTime(&wakeupTime, millis, nanos);
-
+
+ /* wake threads waiting on this record XXX why? */
+
if (lr->waiter)
- wakeWaiters(lr->waiter);
- lr->waiting = true;
+ threads_wake_waiters(lr->waiter);
+
+ /* mark the lock record as "waiting on object o" */
+
+ lr->waiting = o;
STORE_ORDER_BARRIER();
+
+ /* unlock this record */
+
freeLockRecord(lr);
- wasinterrupted = waitWithTimeout(t, lr, &wakeupTime);
- mlr = monitorEnter(t, o);
- removeFromWaiters(mlr, lr);
- mlr->lockCount = lr->lockCount;
+
+ /* wait until notified/interrupted/timed out */
+
+ wasinterrupted = threads_wait_with_timeout(t, &wakeupTime);
+
+ /* re-enter the monitor */
+
+ newlr = monitorEnter(t, o);
+
+ /* we are no longer waiting */
+
+ threads_remove_waiter(newlr, lr);
+ newlr->lockCount = lr->lockCount;
+
+ /* recylce the old lock record */
+
lr->lockCount = 1;
- lr->waiting = false;
+ lr->waiting = NULL;
lr->waiter = NULL;
recycleLockRecord(t, lr);
+ /* if we have been interrupted, throw the appropriate exception */
+
if (wasinterrupted)
*exceptionptr = new_exception(string_java_lang_InterruptedException);
}
+/* notifyOneOrAll **************************************************************
+
+ Notify one thread or all threads waiting on the given object.
+
+ IN:
+ t............the current thread
+ o............the object
+ one..........if true, only notify one thread
+
+ PRE-CONDITION:
+ The current thread must be the owner of the object's monitor.
+
+*******************************************************************************/
+
static void notifyOneOrAll(threadobject *t, java_objectheader *o, bool one)
{
- monitorLockRecord *lr = o->monitorPtr;
+ monitorLockRecord *lr;
+ monitorLockRecord *wlr;
+ threadobject *wthread;
+
+ lr = o->monitorPtr;
GRAB_LR(lr, t);
CHECK_MONITORSTATE(lr, t, o, return);
- do {
- threadobject *wthread;
- monitorLockRecord *wlr = lr->waiter;
- if (!wlr)
- break;
+
+ /* { the thread t owns the lock record lr on the object o } */
+
+ /* for each waiter: */
+
+ for (wlr = lr->waiter; wlr; wlr = wlr->waiter) {
+
+ /* signal the waiting thread */
+
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);
+
+ /* if we should only wake one, we are done */
+
+ if (one)
+ break;
+ }
}
+/* threadHoldsLock *************************************************************
+
+ Return true if the given thread owns the monitor of the given object.
+
+ IN:
+ t............the thread
+ o............the object
+
+ RETURN VALUE:
+ true, if the thread is locking the object
+
+*******************************************************************************/
+
bool threadHoldsLock(threadobject *t, java_objectheader *o)
{
- monitorLockRecord *lr = o->monitorPtr;
+ monitorLockRecord *lr;
+
+ lr = o->monitorPtr;
GRAB_LR(lr, t);
- return lr && lr->o == o && lr->ownerThread == t;
+
+ return (lr->o == o) && (lr->ownerThread == t);
}
+/* interruptThread *************************************************************
+
+ 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 interruptThread(java_lang_VMThread *thread)
{
threadobject *t = (threadobject*) thread;
- t->interrupted = true;
+ /* signal the thread a "waitCond" and tell it that it has been */
+ /* interrupted */
+
pthread_mutex_lock(&t->waitLock);
if (t->isSleeping)
pthread_cond_signal(&t->waitCond);
+ t->interrupted = true;
pthread_mutex_unlock(&t->waitLock);
}
+/* interruptedThread ***********************************************************
+
+ Check if the current thread has been interrupted and reset the
+ interruption flag.
+
+ RETURN VALUE:
+ true, if the current thread had been interrupted
+
+*******************************************************************************/
+
bool interruptedThread()
{
- threadobject *t = (threadobject*) THREADOBJECT;
- bool intr = t->interrupted;
+ threadobject *t;
+ bool intr;
+
+ t = (threadobject*) THREADOBJECT;
+
+ intr = t->interrupted;
+
t->interrupted = false;
+
return intr;
}
+/* isInterruptedThread *********************************************************
+
+ Check if the given thread has been interrupted
+
+ IN:
+ t............the thread to check
+
+ RETURN VALUE:
+ true, if the given thread had been interrupted
+
+*******************************************************************************/
+
bool isInterruptedThread(java_lang_VMThread *thread)
{
- threadobject *t = (threadobject*) thread;
+ threadobject *t;
+
+ t = (threadobject*) thread;
+
return t->interrupted;
}
-void sleepThread(s8 millis, s4 nanos)
+/* thread_sleep ****************************************************************
+
+ Sleep the current thread for the specified amount of time.
+
+*******************************************************************************/
+
+void thread_sleep(s8 millis, s4 nanos)
{
- bool wasinterrupted;
- threadobject *t = (threadobject*) THREADOBJECT;
- monitorLockRecord *lr;
- struct timespec wakeupTime;
+ threadobject *t;
+ struct timespec wakeupTime;
+ bool wasinterrupted;
+
+ t = (threadobject *) THREADOBJECT;
+
calcAbsoluteTime(&wakeupTime, millis, nanos);
- lr = allocLockRecordSimple(t);
- wasinterrupted = waitWithTimeout(t, lr, &wakeupTime);
- recycleLockRecord(t, lr);
+ wasinterrupted = threads_wait_with_timeout(t, &wakeupTime);
if (wasinterrupted)
*exceptionptr = new_exception(string_java_lang_InterruptedException);
void setPriorityThread(thread *t, s4 priority)
{
nativethread *info = &((threadobject*) t->vmThread)->info;
- setPriority(info->tid, priority);
+ threads_set_thread_priority(info->tid, priority);
}
-void wait_cond_for_object(java_objectheader *o, s8 time, s4 nanos)
+void wait_cond_for_object(java_objectheader *o, s8 millis, s4 nanos)
{
threadobject *t = (threadobject*) THREADOBJECT;
- monitorWait(t, o, time, nanos);
+ monitorWait(t, o, millis, nanos);
}
void signal_cond_for_object(java_objectheader *o)
}
+/* 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;
+ 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;
+ 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 */
+
+ thread_sleep(10, 0);
+ }
+
+ tobj = tobj->info.next;
+ } while (tobj && (tobj != mainthreadobj));
+}
+
+
/*
* 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
* c-basic-offset: 4
* tab-width: 4
* End:
+ * vim:noexpandtab:sw=4:ts=4:
*/