* src/threads/native/threads.c (threads_preinit): We always need
[cacao.git] / src / threads / native / threads.c
index e7841220bc1b90954524374399cdf10895b6d5dd..59f9b72d624a829049844bb765fec89b15a2c07c 100644 (file)
@@ -22,7 +22,7 @@
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   $Id: threads.c 7338 2007-02-13 00:17:22Z twisti $
+   $Id: threads.c 7809 2007-04-25 15:14:34Z twisti $
 
 */
 
 
 #include "vmcore/options.h"
 
+#if defined(ENABLE_STATISTICS)
+# include "vmcore/statistics.h"
+#endif
+
 #if !defined(__DARWIN__)
 # if defined(__LINUX__)
 #  define GC_LINUX_THREADS
@@ -236,15 +240,16 @@ pthread_key_t threads_current_threadobject_key;
 /* global threads table                                                       */
 static threads_table_t threads_table;
 
-/* global compiler mutex                                                      */
-static pthread_mutex_t compiler_mutex;
-
 /* global mutex for changing the thread list                                  */
 static pthread_mutex_t threadlistlock;
 
 /* global mutex for stop-the-world                                            */
 static pthread_mutex_t stopworldlock;
 
+/* global mutex and condition for joining threads on exit */
+static pthread_mutex_t mutex_join;
+static pthread_cond_t  cond_join;
+
 /* this is one of the STOPWORLD_FROM_ constants, telling why the world is     */
 /* being stopped                                                              */
 static volatile int stopworldwhere;
@@ -346,30 +351,6 @@ void threads_sem_post(sem_t *sem)
 }
 
 
-/* compiler_lock ***************************************************************
-
-   Enter the compiler lock.
-
-******************************************************************************/
-
-void compiler_lock(void)
-{
-       pthread_mutex_lock(&compiler_mutex);
-}
-
-
-/* compiler_unlock *************************************************************
-
-   Release the compiler lock.
-
-******************************************************************************/
-
-void compiler_unlock(void)
-{
-       pthread_mutex_unlock(&compiler_mutex);
-}
-
-
 /* lock_stopworld **************************************************************
 
    Enter the stopworld lock, specifying why the world shall be stopped.
@@ -619,20 +600,20 @@ static void threads_set_current_threadobject(threadobject *thread)
 
 static void threads_init_threadobject(threadobject *thread)
 {
+       /* get the pthread id */
+
        thread->tid = pthread_self();
 
        thread->index = 0;
 
        /* TODO destroy all those things */
-       pthread_mutex_init(&(thread->joinmutex), NULL);
-       pthread_cond_init(&(thread->joincond), NULL);
 
        pthread_mutex_init(&(thread->waitmutex), NULL);
        pthread_cond_init(&(thread->waitcond), NULL);
 
        thread->interrupted = false;
-       thread->signaled = false;
-       thread->sleeping = false;
+       thread->signaled    = false;
+       thread->sleeping    = false;
 }
 
 
@@ -662,16 +643,27 @@ threadobject *threads_get_current_threadobject(void)
 
 void threads_preinit(void)
 {
-       pthread_mutexattr_t mutexattr;
-       pthread_mutexattr_init(&mutexattr);
-       pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
-       pthread_mutex_init(&compiler_mutex, &mutexattr);
-       pthread_mutexattr_destroy(&mutexattr);
-
        pthread_mutex_init(&threadlistlock, NULL);
        pthread_mutex_init(&stopworldlock, NULL);
 
+       /* initialize exit mutex and condition (on exit we join all
+          threads) */
+
+       pthread_mutex_init(&mutex_join, NULL);
+       pthread_cond_init(&cond_join, NULL);
+
+#if defined(ENABLE_GC_BOEHM)
+       mainthreadobj = GCNEW_UNCOLLECTABLE(threadobject, 1);
+#else
        mainthreadobj = NEW(threadobject);
+#endif
+
+#if defined(ENABLE_STATISTICS)
+       if (opt_stat)
+               size_threadobject += sizeof(threadobject);
+#endif
+
+       mainthreadobj->object   = NULL;
        mainthreadobj->tid      = pthread_self();
        mainthreadobj->index    = 1;
        mainthreadobj->thinlock = lock_pre_compute_thinlock(mainthreadobj->index);
@@ -703,32 +695,19 @@ void threads_preinit(void)
 
 bool threads_init(void)
 {
-       java_lang_String      *threadname;
-       threadobject          *tempthread;
+       java_objectheader     *threadname;
+       java_lang_Thread      *t;
        java_objectheader     *o;
 
 #if defined(ENABLE_JAVASE)
        java_lang_ThreadGroup *threadgroup;
        methodinfo            *m;
-       java_lang_Thread      *t;
 #endif
 
 #if defined(WITH_CLASSPATH_GNU)
        java_lang_VMThread    *vmt;
 #endif
 
-       tempthread = mainthreadobj;
-
-       /* XXX We have to find a new way to free lock records */
-       /*     with the new locking algorithm.                */
-       /* lock_record_free_pools(mainthreadobj->ee.lockrecordpools); */
-
-       /* 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_Thread->instancesize = sizeof(threadobject);
-
        /* get methods we need in this file */
 
 #if defined(WITH_CLASSPATH_GNU)
@@ -752,19 +731,23 @@ bool threads_init(void)
 
        /* create a java.lang.Thread for the main thread */
 
-       mainthreadobj = (threadobject *) builtin_new(class_java_lang_Thread);
+       t = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
 
-       if (mainthreadobj == NULL)
+       if (t == NULL)
                return false;
 
-       FREE(tempthread, threadobject);
+       /* set the object in the internal data structure */
 
-       threads_init_threadobject(mainthreadobj);
+       mainthreadobj->object = t;
 
+       threads_init_threadobject(mainthreadobj);
        threads_set_current_threadobject(mainthreadobj);
-
        lock_init_execution_env(mainthreadobj);
 
+       /* thread is running */
+
+       mainthreadobj->state = THREAD_STATE_RUNNABLE;
+
        mainthreadobj->next = mainthreadobj;
        mainthreadobj->prev = mainthreadobj;
 
@@ -792,7 +775,7 @@ bool threads_init(void)
                native_new_and_init(class_java_lang_ThreadGroup);
 
        if (threadgroup == NULL)
-               throw_exception_exit();
+               return false;
 #endif
 
 #if defined(WITH_CLASSPATH_GNU)
@@ -801,22 +784,26 @@ bool threads_init(void)
        vmt = (java_lang_VMThread *) builtin_new(class_java_lang_VMThread);
 
        if (vmt == NULL)
-               throw_exception_exit();
+               return false;
 
        /* set the thread */
 
-       t           = (java_lang_Thread *) mainthreadobj;
        vmt->thread = t;
+       vmt->vmdata = (java_lang_Object *) mainthreadobj;
 
        /* call java.lang.Thread.<init>(Ljava/lang/VMThread;Ljava/lang/String;IZ)V */
-       o = (java_objectheader *) mainthreadobj;
+       o = (java_objectheader *) t;
 
        (void) vm_call_method(method_thread_init, o, vmt, threadname, NORM_PRIORITY,
                                                  false);
 #elif defined(WITH_CLASSPATH_CLDC1_1)
+       /* set the thread */
+
+       t->vm_thread = (java_lang_Object *) mainthreadobj;
+
        /* call public Thread(String name) */
 
-       o = (java_objectheader *) mainthreadobj;
+       o = (java_objectheader *) t;
 
        (void) vm_call_method(method_thread_init, o, threadname);
 #endif
@@ -825,7 +812,7 @@ bool threads_init(void)
                return false;
 
 #if defined(ENABLE_JAVASE)
-       mainthreadobj->o.group = threadgroup;
+       t->group = threadgroup;
 
        /* add main thread to java.lang.ThreadGroup */
 
@@ -836,13 +823,11 @@ bool threads_init(void)
                                                                 true);
 
        o = (java_objectheader *) threadgroup;
-       t = (java_lang_Thread *) mainthreadobj;
 
        (void) vm_call_method(m, o, t);
 
        if (*exceptionptr)
                return false;
-
 #endif
 
        threads_set_thread_priority(pthread_self(), NORM_PRIORITY);
@@ -1052,6 +1037,10 @@ static void *threads_startup_thread(void *t)
 #endif
        threads_set_current_threadobject(thread);
 
+       /* thread is running */
+
+       thread->state = THREAD_STATE_RUNNABLE;
+
        /* insert the thread into the threadlist and the threads table */
 
        pthread_mutex_lock(&threadlistlock);
@@ -1077,7 +1066,7 @@ static void *threads_startup_thread(void *t)
 
        /* set our priority */
 
-       threads_set_thread_priority(thread->tid, thread->o.priority);
+       threads_set_thread_priority(thread->tid, thread->object->priority);
 
 #if defined(ENABLE_INTRP)
        /* set interpreter stack */
@@ -1107,13 +1096,13 @@ static void *threads_startup_thread(void *t)
 
                c   = class_java_lang_VMThread;
 #elif defined(WITH_CLASSPATH_CLDC1_1)
-               c   = thread->o.header.vftbl->class;
+               c   = thread->object->header.vftbl->class;
 #endif
 
                m = class_resolveclassmethod(c, utf_run, utf_void__void, c, true);
 
                if (m == NULL)
-                       throw_exception();
+                       vm_abort("threads_startup_thread: run() method not found in class");
 
                /* set ThreadMXBean variables */
 
@@ -1128,13 +1117,15 @@ static void *threads_startup_thread(void *t)
 #if defined(WITH_CLASSPATH_GNU)
                /* we need to start the run method of java.lang.VMThread */
 
-               vmt = (java_lang_VMThread *) thread->o.vmThread;
+               vmt = (java_lang_VMThread *) thread->object->vmThread;
                o   = (java_objectheader *) vmt;
 
 #elif defined(WITH_CLASSPATH_CLDC1_1)
-               o   = (java_objectheader *) thread;
+               o   = (java_objectheader *) thread->object;
 #endif
 
+               /* run the thread */
+
                (void) vm_call_method(m, o);
        }
        else {
@@ -1164,7 +1155,8 @@ static void *threads_startup_thread(void *t)
                jvmti_ThreadStartEnd(JVMTI_EVENT_THREAD_END);
 #endif
 
-       threads_detach_thread(thread);
+       if (!threads_detach_thread(thread))
+               vm_abort("threads_startup_thread: threads_detach_thread failed");
 
        /* set ThreadMXBean variables */
 
@@ -1176,7 +1168,7 @@ static void *threads_startup_thread(void *t)
 
 /* threads_start_thread ********************************************************
 
-   Start a thread in the JVM.
+   Start a thread in the JVM. Both (vm internal and java) thread objects exist.
 
    IN:
       thread.......the thread object
@@ -1248,14 +1240,6 @@ void threads_set_thread_priority(pthread_t tid, int priority)
        struct sched_param schedp;
        int policy;
 
-#if defined(ENABLE_JAVAME_CLDC1_1)
-       /* The thread id is zero when a thread is created in Java.  The
-          priority is set later during startup. */
-
-       if (tid == NULL)
-               return;
-#endif
-
        pthread_getschedparam(tid, &policy, &schedp);
        schedp.sched_priority = priority;
        pthread_setschedparam(tid, policy, &schedp);
@@ -1272,7 +1256,7 @@ bool threads_attach_current_thread(JavaVMAttachArgs *vm_aargs, bool isdaemon)
 {
        threadobject          *thread;
        utf                   *u;
-       java_lang_String      *s;
+       java_objectheader     *s;
        java_objectheader     *o;
        java_lang_Thread      *t;
 
@@ -1285,21 +1269,39 @@ bool threads_attach_current_thread(JavaVMAttachArgs *vm_aargs, bool isdaemon)
        java_lang_VMThread    *vmt;
 #endif
 
-       /* create a java.lang.Thread object */
+       /* create a vm internal thread object */
 
-       thread = (threadobject *) builtin_new(class_java_lang_Thread);
+#if defined(ENABLE_GC_BOEHM)
+       thread = GCNEW_UNCOLLECTABLE(threadobject, 1);
+#else
+       thread = NEW(threadobject);
+#endif
+
+#if defined(ENABLE_STATISTICS)
+       if (opt_stat)
+               size_threadobject += sizeof(threadobject);
+#endif
 
        if (thread == NULL)
                return false;
 
-       /* cast for convenience */
+       /* create a java.lang.Thread object */
+
+       t = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
 
-       t = (java_lang_Thread *) thread;
+       if (t == NULL)
+               return false;
+
+       thread->object = t;
 
        threads_init_threadobject(thread);
        threads_set_current_threadobject(thread);
        lock_init_execution_env(thread);
 
+       /* thread is running */
+
+       thread->state = THREAD_STATE_RUNNABLE;
+
        /* insert the thread into the threadlist and the threads table */
 
        pthread_mutex_lock(&threadlistlock);
@@ -1340,6 +1342,9 @@ bool threads_attach_current_thread(JavaVMAttachArgs *vm_aargs, bool isdaemon)
        /* set the thread */
 
        vmt->thread = t;
+       vmt->vmdata = (java_lang_Object *) thread;
+#elif defined(WITH_CLASSPATH_CLDC1_1)
+       t->vm_thread = (java_lang_Object *) thread;
 #endif
 
        if (vm_aargs != NULL) {
@@ -1351,13 +1356,17 @@ bool threads_attach_current_thread(JavaVMAttachArgs *vm_aargs, bool isdaemon)
        else {
                u     = utf_null;
 #if defined(ENABLE_JAVASE)
-               group = mainthreadobj->o.group;
+               group = mainthreadobj->object->group;
 #endif
        }
 
+       /* the the thread name */
+
        s = javastring_new(u);
 
-       o = (java_objectheader *) thread;
+       /* for convenience */
+
+       o = (java_objectheader *) thread->object;
 
 #if defined(WITH_CLASSPATH_GNU)
        (void) vm_call_method(method_thread_init, o, vmt, s, NORM_PRIORITY,
@@ -1372,7 +1381,7 @@ bool threads_attach_current_thread(JavaVMAttachArgs *vm_aargs, bool isdaemon)
 #if defined(ENABLE_JAVASE)
        /* store the thread group in the object */
 
-       thread->o.group = group;
+       thread->object->group = group;
 
        /* add thread to given thread-group */
 
@@ -1421,7 +1430,7 @@ bool threads_detach_thread(threadobject *thread)
 #if defined(ENABLE_JAVASE)
        /* remove thread from the thread group */
 
-       group = thread->o.group;
+       group = thread->object->group;
 
        /* XXX TWISTI: should all threads be in a ThreadGroup? */
 
@@ -1436,7 +1445,7 @@ bool threads_detach_thread(threadobject *thread)
                        return false;
 
                o = (java_objectheader *) group;
-               t = (java_lang_Thread *) thread;
+               t = thread->object;
 
                (void) vm_call_method(m, o, t);
 
@@ -1445,27 +1454,43 @@ bool threads_detach_thread(threadobject *thread)
        }
 #endif
 
-       /* remove thread from thread list and threads table, do this
-          inside a lock */
+       /* thread is terminated */
+
+       thread->state = THREAD_STATE_TERMINATED;
+
+       /* lock thread list */
 
        pthread_mutex_lock(&threadlistlock);
 
+       /* remove thread from thread list and threads table */
+
        thread->next->prev = thread->prev;
        thread->prev->next = thread->next;
 
        threads_table_remove(thread);
 
+       /* unlock thread list */
+
        pthread_mutex_unlock(&threadlistlock);
 
-       /* reset thread id (lock on joinmutex? TWISTI) */
+       /* signal that this thread has finished */
 
-       pthread_mutex_lock(&(thread->joinmutex));
-       thread->tid = 0;
-       pthread_mutex_unlock(&(thread->joinmutex));
+       pthread_mutex_lock(&mutex_join);
+       pthread_cond_signal(&cond_join);
+       pthread_mutex_unlock(&mutex_join);
 
-       /* tell everyone that a thread has finished */
+       /* free the vm internal thread object */
 
-       pthread_cond_broadcast(&(thread->joincond));
+#if defined(ENABLE_GC_BOEHM)
+       GCFREE(thread);
+#else
+       FREE(thread, threadobject);
+#endif
+
+#if defined(ENABLE_STATISTICS)
+       if (opt_stat)
+               size_threadobject -= sizeof(threadobject);
+#endif
 
        return true;
 }
@@ -1478,18 +1503,34 @@ bool threads_detach_thread(threadobject *thread)
 
 *******************************************************************************/
 
-/* At the end of the program, we wait for all running non-daemon
-   threads to die. */
-
-static threadobject *threads_find_non_daemon_thread(threadobject *thread)
+static threadobject *threads_find_non_daemon_thread(void)
 {
+       threadobject *thread;
+
+       /* lock the thread list */
+
+       pthread_mutex_lock(&threadlistlock);
+
+       /* iterate over all threads */
+
+       thread = mainthreadobj->next;
+
        while (thread != mainthreadobj) {
-               if (!(thread->flags & THREAD_FLAG_DAEMON))
+               if (!(thread->flags & THREAD_FLAG_DAEMON)) {
+                       /* unlock thread list */
+
+                       pthread_mutex_unlock(&threadlistlock);
+
                        return thread;
+               }
 
-               thread = thread->prev;
+               thread = thread->next;
        }
 
+       /* unlock thread list */
+
+       pthread_mutex_unlock(&threadlistlock);
+
        return NULL;
 }
 
@@ -1504,22 +1545,26 @@ void threads_join_all_threads(void)
 {
        threadobject *thread;
 
-       pthread_mutex_lock(&threadlistlock);
+       /* get current thread */
 
-       while ((thread = threads_find_non_daemon_thread(mainthreadobj->prev)) != NULL) {
-               pthread_mutex_lock(&(thread->joinmutex));
+       thread = THREADOBJECT;
 
-               pthread_mutex_unlock(&threadlistlock);
+       /* this thread is waiting for all non-daemon threads to exit */
 
-               while (thread->tid)
-                       pthread_cond_wait(&(thread->joincond), &(thread->joinmutex));
+       thread->state = THREAD_STATE_WAITING;
 
-               pthread_mutex_unlock(&(thread->joinmutex));
+       /* enter join mutex */
 
-               pthread_mutex_lock(&threadlistlock);
-       }
+       pthread_mutex_lock(&mutex_join);
 
-       pthread_mutex_unlock(&threadlistlock);
+       /* wait for condition as long as we have non-daemon threads */
+
+       while (threads_find_non_daemon_thread() != NULL)
+               pthread_cond_wait(&cond_join, &mutex_join);
+
+       /* leave join mutex */
+
+       pthread_mutex_unlock(&mutex_join);
 }
 
 
@@ -1615,14 +1660,23 @@ static bool threads_wait_with_timeout(threadobject *thread,
                while (!thread->interrupted && !thread->signaled
                           && threads_current_time_is_earlier_than(wakeupTime))
                {
+                       thread->state = THREAD_STATE_TIMED_WAITING;
+
                        pthread_cond_timedwait(&thread->waitcond, &thread->waitmutex,
                                                                   wakeupTime);
+
+                       thread->state = THREAD_STATE_RUNNABLE;
                }
        }
        else {
                /* no timeout */
-               while (!thread->interrupted && !thread->signaled)
+               while (!thread->interrupted && !thread->signaled) {
+                       thread->state = THREAD_STATE_WAITING;
+
                        pthread_cond_wait(&thread->waitcond, &thread->waitmutex);
+
+                       thread->state = THREAD_STATE_RUNNABLE;
+               }
        }
 
        /* check if we were interrupted */
@@ -1820,66 +1874,6 @@ void threads_yield(void)
 }
 
 
-/* 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     *thread;
-       java_lang_Thread *t;
-       utf              *name;
-
-       thread = mainthreadobj;
-
-       /* XXX we should stop the world here */
-
-       printf("Full thread dump CACAO "VERSION":\n");
-
-       /* iterate over all started threads */
-
-       do {
-               /* get thread object */
-
-               t = (java_lang_Thread *) thread;
-
-               /* the thread may be currently in initalization, don't print it */
-
-               if (t != NULL) {
-                       /* get thread name */
-
-#if defined(ENABLE_JAVASE)
-                       name = javastring_toutf(t->name, false);
-#elif defined(ENABLE_JAVAME_CLDC1_1)
-                       name = t->name;
-#endif
-
-                       printf("\n\"");
-                       utf_display_printable_ascii(name);
-                       printf("\" ");
-
-                       if (thread->flags & THREAD_FLAG_DAEMON)
-                               printf("daemon ");
-
-#if SIZEOF_VOID_P == 8
-                       printf("prio=%d tid=0x%016lx\n", t->priority, (ptrint) thread->tid);
-#else
-                       printf("prio=%d tid=0x%08lx\n", t->priority, (ptrint) thread->tid);
-#endif
-
-                       /* dump trace of thread */
-
-                       stacktrace_dump_trace(thread);
-               }
-
-               thread = thread->next;
-       } while ((thread != NULL) && (thread != mainthreadobj));
-}
-
-
 /* threads_table_dump *********************************************************
 
    Dump the threads table for debugging purposes.