More NATIVE_THREADS ifdefs
[cacao.git] / threads / thread.c
index 97e18c11f743a4b1213351e506ba0f8a8a48f5ff..7ae98e6d87ee5ce65b4fcff2a97a9b9c48f86396 100644 (file)
  * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
  */
 
-#include <assert.h>
 
+#include "global.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
 #include <sys/types.h>
 #include <sys/mman.h>                   /* for mprotect */
 #include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
 
+#include "config.h"
 #include "thread.h"
 #include "locks.h"
-#include "sysdep/defines.h"
-#include "sysdep/threads.h"
+#include "tables.h"
+#include "native.h"
+#include "loader.h"
+#include "builtin.h"
+#include "asmpart.h"
+#include "toolbox/loging.h"
+#include "toolbox/memory.h"
+#include "toolbox/avl.h"
 
-#include "../tables.h"
-#include "../native.h"
-#include "../loader.h"
-#include "../builtin.h"
-#include "../asmpart.h"
+#if defined(NATIVE_THREADS)
 
-static classinfo *class_java_lang_ThreadDeath;
+static struct avl_table *criticaltree;
+
+static int criticalcompare(const void *pa, const void *pb, void *param)
+{
+       const threadcritnode *na = pa;
+       const threadcritnode *nb = pb;
+
+       if (na->mcodebegin < nb->mcodebegin)
+               return -1;
+       if (na->mcodebegin > nb->mcodebegin)
+               return 1;
+       return 0;
+}
 
-#if 1
-#define DBG(s)
-#define SDBG(s)
-#else
-#define DBG(s)                 s
-#define SDBG(s)                s
+static const threadcritnode *findcritical(u1 *mcodeptr)
+{
+    struct avl_node *n = criticaltree->avl_root;
+    const threadcritnode *m = NULL;
+    if (!n)
+        return NULL;
+    for (;;)
+    {
+        const threadcritnode *d = n->avl_data;
+        if (mcodeptr == d->mcodebegin)
+            return d;
+        if (mcodeptr < d->mcodebegin) {
+            if (n->avl_link[0])
+                n = n->avl_link[0];
+            else
+                return m;
+        } else {
+            if (n->avl_link[1]) {
+                m = n->avl_data;
+                n = n->avl_link[1];
+            } else
+                return n->avl_data;
+        }
+    }
+}
+
+void thread_registercritical(threadcritnode *n)
+{
+       avl_insert(criticaltree, n);
+}
+
+static int thread_checkcritical(u1 *mcodeptr)
+{
+       const threadcritnode *n = findcritical(mcodeptr);
+       if (!n)
+               return 0;
+       return (mcodeptr < n->mcodeend);
+}
+
+pthread_mutex_t cast_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t compiler_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+int cast_counter;
+
+#ifndef HAVE___THREAD
+pthread_key_t tkey_threadinfo;
 #endif
 
-#if defined(USE_INTERNAL_THREADS)
+void cast_lock()
+{
+}
+
+void cast_unlock()
+{
+}
+
+#else // !defined(NATIVE_THREADS)
+
+static classinfo *class_java_lang_ThreadDeath;
 
 thread* currentThread = NULL;
 thread* mainThread;
@@ -46,7 +116,7 @@ thread* threadQhead[MAX_THREAD_PRIO + 1];
 thread* threadQtail[MAX_THREAD_PRIO + 1];
 
 thread* liveThreads = NULL;
-thread* alarmList;
+thread* sleepThreads = NULL;
 
 int blockInts;
 bool needReschedule;
@@ -66,6 +136,9 @@ void reschedule(void);
 /* Setup default thread stack size - this can be overwritten if required */
 int threadStackSize = THREADSTACKSIZE;
 
+/* Pointer to the stack of the last killed thread. The free is delayed. */
+void *stack_to_be_freed = 0;
+
 static thread* startDaemon(void* func, char* nm, int stackSize);
 
 /*
@@ -74,43 +147,39 @@ static thread* startDaemon(void* func, char* nm, int stackSize);
 void
 allocThreadStack (thread *tid, int size)
 {
-    int pageSize = getpagesize(),
-               result;
+    int pageSize = getpagesize();
     unsigned long pageBegin;
 
-    CONTEXT(tid).stackMem = malloc(size + 2 * pageSize);
+       assert(stack_to_be_freed == 0);
+
+    CONTEXT(tid).stackMem = GCNEW(u1, size + 4 * pageSize);
     assert(CONTEXT(tid).stackMem != 0);
     CONTEXT(tid).stackEnd = CONTEXT(tid).stackMem + size + 2 * pageSize;
     
     pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
     pageBegin = pageBegin - pageBegin % pageSize;
 
-    result = mprotect((void*)pageBegin, pageSize, PROT_NONE);
-    assert(result == 0);
-
     CONTEXT(tid).stackBase = (u1*)pageBegin + pageSize;
 }
 
 /*
- * Free the stack for a thread
+ * Mark the stack for a thread to be freed. We cannot free the stack
+ * immediately because it is still in use!
  */
 void
 freeThreadStack (thread *tid)
 {
     if (!(CONTEXT(tid).flags & THREAD_FLAGS_NOSTACKALLOC))
     {
-               int pageSize = getpagesize(),
-                       result;
+               int pageSize = getpagesize();
                unsigned long pageBegin;
 
                pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
                pageBegin = pageBegin - pageBegin % pageSize;
-       
-               result = mprotect((void*)pageBegin, pageSize,
-                                                 PROT_READ | PROT_WRITE | PROT_EXEC);
-               assert(result == 0);
 
-               free(CONTEXT(tid).stackMem);
+               assert(stack_to_be_freed == 0);
+
+               stack_to_be_freed = CONTEXT(tid).stackMem;
     }
     CONTEXT(tid).stackMem = 0;
     CONTEXT(tid).stackBase = 0;
@@ -123,26 +192,58 @@ freeThreadStack (thread *tid)
 void
 initThreads(u1 *stackbottom)
 {
+#if defined(NATIVE_THREADS)
+#if !defined(HAVE___THREAD)
+       pthread_key_create(&tkey_threadinfo, NULL);
+#endif
+
+    criticaltree = avl_create(criticalcompare, NULL, NULL);
+
+#endif
+
        thread *the_main_thread;
     int i;
+       char mainname[] = "main";
+       /*int len = strlen(mainname);*/
+
+       signal(SIGPIPE, SIG_IGN);
 
     initLocks();
 
-    for (i = 0; i < MAXTHREADS; ++i)
+    for (i = 0; i < MAXTHREADS; ++i) {
                contexts[i].free = true;
+               contexts[i].thread = NULL;
+       }
 
     /* Allocate a thread to be the main thread */
-    liveThreads = the_main_thread = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
+    liveThreads = the_main_thread = (thread*)builtin_new(loader_load_sysclass(NULL,utf_new_char("java/lang/Thread")));
     assert(the_main_thread != 0);
-       heap_addreference((void **) &liveThreads);
     
     the_main_thread->PrivateInfo = 1;
     CONTEXT(the_main_thread).free = false;
 
-    the_main_thread->name = javastring_new(unicode_new_char("main"));
+#if 0
+    {
+       /* stefan */
+       methodinfo *m;
+       m = class_fetchmethod(
+                       class_java_lang_String,
+                       utf_new_char ("toCharArray"),
+                       utf_new_char ("()[C")
+                       );
+printf("DEADCODE LIVES ?????????\n");fflush(stdout);
+       the_main_thread->name = asm_calljavafunction (m, javastring_new(utf_new_char("main")), 0, 0, 0);
+    }
+#endif
+       the_main_thread->name=javastring_new(utf_new_char(mainname));
+/*     the_main_thread->name = builtin_newarray_char(len);
+       {   u2 *d = the_main_thread->name->data;
+               for (i=0; i<len; i++)
+                       d[i] = mainname[i];
+       }*/
     the_main_thread->priority = NORM_THREAD_PRIO;
     CONTEXT(the_main_thread).priority = (u1)the_main_thread->priority;
-    CONTEXT(the_main_thread).exceptionptr = 0;
+    CONTEXT(the_main_thread).texceptionptr = 0;
     the_main_thread->next = 0;
     CONTEXT(the_main_thread).status = THREAD_SUSPENDED;
     CONTEXT(the_main_thread).stackBase = CONTEXT(the_main_thread).stackEnd = stackbottom;
@@ -155,20 +256,25 @@ initThreads(u1 *stackbottom)
 
        CONTEXT(the_main_thread).flags = THREAD_FLAGS_NOSTACKALLOC;
        CONTEXT(the_main_thread).nextlive = 0;
-       the_main_thread->single_step = 0;
+       CONTEXT(the_main_thread).thread = the_main_thread;
+       /*the_main_thread->single_step = 0;*/
        the_main_thread->daemon = 0;
-       the_main_thread->stillborn = 0;
-       the_main_thread->target = 0;
-       the_main_thread->interruptRequested = 0;
-       the_main_thread->group =
-               (threadGroup*)builtin_new(loader_load(unicode_new_char("java/lang/ThreadGroup")));
-       /* we should call the constructor */
+       /*the_main_thread->stillborn = 0;*/
+       /*the_main_thread->target = 0;*/
+
+       the_main_thread->contextClassLoader = 0;
+       /*the_main_thread->inheritedAccessControlContext = 0;*/
+       /*the_main_thread->values = 0;*/
+
+       /* Allocate and init ThreadGroup */
+       the_main_thread->group = (threadGroup*)native_new_and_init(loader_load(utf_new_char("java/lang/ThreadGroup")));
        assert(the_main_thread->group != 0);
 
        talive++;
 
        /* Load exception classes */
-       class_java_lang_ThreadDeath = loader_load(unicode_new_char("java/lang/ThreadDeath"));
+    loader_load_sysclass(&class_java_lang_ThreadDeath,
+                         utf_new_char("java/lang/ThreadDeath"));
 
        DBG( fprintf(stderr, "finishing initThreads\n"); );
 
@@ -196,8 +302,11 @@ startThread (thread* tid)
     if (i == MAXTHREADS)
                panic("Too many threads");
 
+       assert(tid->priority >= MIN_THREAD_PRIO && tid->priority <= MAX_THREAD_PRIO);
+
     tid->PrivateInfo = i + 1;
     CONTEXT(tid).free = false;
+       CONTEXT(tid).thread = tid;
     CONTEXT(tid).nextlive = liveThreads;
     liveThreads = tid;
     allocThreadStack(tid, threadStackSize);
@@ -205,7 +314,7 @@ startThread (thread* tid)
     CONTEXT(tid).flags = THREAD_FLAGS_GENERAL;
     CONTEXT(tid).status = THREAD_SUSPENDED;
     CONTEXT(tid).priority = (u1)tid->priority;
-    CONTEXT(tid).exceptionptr = 0;
+    CONTEXT(tid).texceptionptr = 0;
 
     /* Construct the initial restore point. */
     THREADINIT((&CONTEXT(tid)), firstStartThread);
@@ -233,7 +342,7 @@ startDaemon(void* func, char* nm, int stackSize)
 
     DBG( printf("startDaemon %s\n", nm); );
 
-       tid = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
+       tid = (thread*)builtin_new(loader_load_sysclass(NULL,utf_new_char("java/lang/Thread")));
        assert(tid != 0);
 
        for (i = 0; i < MAXTHREADS; ++i)
@@ -251,11 +360,10 @@ startDaemon(void* func, char* nm, int stackSize)
        CONTEXT(tid).status = THREAD_SUSPENDED;
 
        allocThreadStack(tid, stackSize);
-       tid->single_step = 0;
+       /*tid->single_step = 0;*/
        tid->daemon = 1;
-       tid->stillborn = 0;
-       tid->target = 0;
-       tid->interruptRequested = 0;
+       /*tid->stillborn = 0;*/
+       /*tid->target = 0;*/
        tid->group = 0;
 
        /* Construct the initial restore point. */
@@ -277,16 +385,26 @@ firstStartThread(void)
 
     DBG( printf("firstStartThread %p\n", currentThread); );
 
+       if (stack_to_be_freed != 0)     {
+               stack_to_be_freed = 0;
+       }
+
        /* Every thread starts with the interrupts off */
        intsRestore();
        assert(blockInts == 0);
 
        /* Find the run()V method and call it */
        method = class_findmethod(currentThread->header.vftbl->class,
-                                                         unicode_new_char("run"), unicode_new_char("()V"));
+                                                         utf_new_char("run"), utf_new_char("()V"));
        if (method == 0)
                panic("Cannot find method \'void run ()\'");
-       asm_calljavamethod(method, currentThread, NULL, NULL, NULL);
+
+       asm_calljavafunction(method, currentThread, NULL, NULL, NULL);
+
+    if (*exceptionptr) {
+        utf_display((*exceptionptr)->vftbl->class->name);
+        printf("\n");
+    }
 
        killThread(0);
        assert("Thread returned from killThread" == 0);
@@ -363,7 +481,7 @@ yieldThread()
  * (which is set by suspendThread(.))
  */
 void
-resumeThread(thread* tid)
+resumeThread (thread* tid)
 {
     if ((CONTEXT(tid).flags & THREAD_FLAGS_USER_SUSPEND) != 0)
     {
@@ -433,7 +551,7 @@ suspendOnQThread(thread* tid, thread** queue)
 
        DBG( printf("suspendOnQThread %p %p\n", tid, queue); );
 
-       assert(blockInts == 1);
+       assert(blockInts > 0);
 
        if (CONTEXT(tid).status != THREAD_SUSPENDED)
        {
@@ -506,6 +624,7 @@ killThread(thread* tid)
                /* If we only have daemons left, then everyone is dead. */
                if (talive == tdaemon) {
                        /* atexit functions get called to clean things up */
+                       intsRestore();
                        exit(0);
                }
 
@@ -530,7 +649,10 @@ killThread(thread* tid)
                freeThreadStack(tid);
 
                /* free context */
-               CONTEXT(tid).free = true;
+               if (tid != mainThread) {
+                       CONTEXT(tid).free = true;
+                       CONTEXT(tid).thread = NULL;
+               }
 
                /* Run something else */
                needReschedule = true;
@@ -546,6 +668,8 @@ setPriorityThread(thread* tid, int prio)
 {
     thread** ntid;
 
+       assert(prio >= MIN_THREAD_PRIO && prio <= MAX_THREAD_PRIO);
+
     if (tid->PrivateInfo == 0) {
                tid->priority = prio;
                return;
@@ -585,6 +709,55 @@ setPriorityThread(thread* tid, int prio)
     intsRestore();
 }
 
+/*
+ * Get the current time in milliseconds since 1970-01-01.
+ */
+s8
+currentTime (void)
+{
+       struct timeval tv;
+       s8 time;
+
+       gettimeofday(&tv, 0);
+
+       time = tv.tv_sec;
+       time *= 1000;
+       time += tv.tv_usec / 1000;
+
+       return time;
+}
+
+/*
+ * Put a thread to sleep.
+ */
+void
+sleepThread (s8 time)
+{
+    thread** tidp;
+
+    /* Sleep for no time */
+    if (time <= 0) {
+               return;
+    }
+    
+    intsDisable();
+
+    /* Get absolute time */
+    CONTEXT(currentThread).time = time + currentTime();
+
+    /* Find place in alarm list */
+    for (tidp = &sleepThreads; (*tidp) != 0; tidp = &(*tidp)->next)
+       {
+               if (CONTEXT(*tidp).time > CONTEXT(currentThread).time)
+                       break;
+    }
+
+    /* Suspend thread on it */
+    suspendOnQThread(currentThread, tidp);
+    
+    intsRestore();
+}
+
 /*
  * Is this thread alive?
  */
@@ -633,13 +806,18 @@ reschedule(void)
                                        lastThread = currentThread;
                                        currentThread = threadQhead[i];
 
-                                       CONTEXT(currentThread).exceptionptr = exceptionptr;
+                                       CONTEXT(currentThread).texceptionptr = *exceptionptr;
 
+                    DBG( fprintf(stderr, "thread switch from: %p to: %p\n", lastThread, currentThread); );
                                        THREADSWITCH((&CONTEXT(currentThread)),
                                                                 (&CONTEXT(lastThread)));
                                        blockInts = b;
 
-                                       exceptionptr = CONTEXT(currentThread).exceptionptr;
+                                       *exceptionptr = CONTEXT(currentThread).texceptionptr;
+
+                                       if (stack_to_be_freed != 0) {
+                                               stack_to_be_freed = 0;
+                                       }
 
                                        /* Alarm signal may be blocked - if so
                                         * unblock it.
@@ -658,7 +836,7 @@ reschedule(void)
                                                != 0)
                                        {
                                                CONTEXT(lastThread).flags &= ~THREAD_FLAGS_KILLED;
-                                               exceptionptr = native_new_and_init(class_java_lang_ThreadDeath);
+                                               *exceptionptr = native_new_and_init(class_java_lang_ThreadDeath);
                                        }
                                }
                                /* Now we kill the schedule and turn ints