GC now works without threads being initialized, which caused a
[cacao.git] / threads / thread.c
1 /* -*- mode: c; tab-width: 4; c-basic-offset: 4 -*- */
2 /*
3  * thread.c
4  * Thread support.
5  *
6  * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  *
11  * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
12  */
13
14 #include <assert.h>
15
16 #include <sys/types.h>
17 #include <sys/mman.h>                   /* for mprotect */
18 #include <unistd.h>
19
20 #include "thread.h"
21 #include "locks.h"
22 #include "sysdep/defines.h"
23 #include "sysdep/threads.h"
24
25 #include "../tables.h"
26 #include "../native.h"
27 #include "../loader.h"
28 #include "../builtin.h"
29 #include "../asmpart.h"
30
31 static classinfo *class_java_lang_ThreadDeath;
32
33 #if 1
34 #define DBG(s)
35 #define SDBG(s)
36 #else
37 #define DBG(s)                 s
38 #define SDBG(s)                s
39 #endif
40
41 #if defined(USE_INTERNAL_THREADS)
42
43 thread* currentThread = NULL;
44 thread* mainThread;
45 thread* threadQhead[MAX_THREAD_PRIO + 1];
46 thread* threadQtail[MAX_THREAD_PRIO + 1];
47
48 thread* liveThreads = NULL;
49 thread* alarmList;
50
51 int blockInts;
52 bool needReschedule;
53
54 ctx contexts[MAXTHREADS];
55
56 /* Number of threads alive, also counting daemons */
57 static int talive;
58
59 /* Number of daemon threads alive */
60 static int tdaemon;
61
62 static void firstStartThread(void);
63
64 void reschedule(void);
65
66 /* Setup default thread stack size - this can be overwritten if required */
67 int threadStackSize = THREADSTACKSIZE;
68
69 static thread* startDaemon(void* func, char* nm, int stackSize);
70
71 /*
72  * Allocate the stack for a thread
73  */
74 void
75 allocThreadStack (thread *tid, int size)
76 {
77     int pageSize = getpagesize(),
78                 result;
79     unsigned long pageBegin;
80
81     CONTEXT(tid).stackMem = malloc(size + 2 * pageSize);
82     assert(CONTEXT(tid).stackMem != 0);
83     CONTEXT(tid).stackEnd = CONTEXT(tid).stackMem + size + 2 * pageSize;
84     
85     pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
86     pageBegin = pageBegin - pageBegin % pageSize;
87
88     result = mprotect((void*)pageBegin, pageSize, PROT_NONE);
89     assert(result == 0);
90
91     CONTEXT(tid).stackBase = (u1*)pageBegin + pageSize;
92 }
93
94 /*
95  * Free the stack for a thread
96  */
97 void
98 freeThreadStack (thread *tid)
99 {
100     if (!(CONTEXT(tid).flags & THREAD_FLAGS_NOSTACKALLOC))
101     {
102                 int pageSize = getpagesize(),
103                         result;
104                 unsigned long pageBegin;
105
106                 pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
107                 pageBegin = pageBegin - pageBegin % pageSize;
108         
109                 result = mprotect((void*)pageBegin, pageSize,
110                                                   PROT_READ | PROT_WRITE | PROT_EXEC);
111                 assert(result == 0);
112
113                 free(CONTEXT(tid).stackMem);
114     }
115     CONTEXT(tid).stackMem = 0;
116     CONTEXT(tid).stackBase = 0;
117     CONTEXT(tid).stackEnd = 0;
118 }
119
120 /*
121  * Initialize threads.
122  */
123 void
124 initThreads(u1 *stackbottom)
125 {
126         thread *the_main_thread;
127     int i;
128
129     initLocks();
130
131     for (i = 0; i < MAXTHREADS; ++i)
132                 contexts[i].free = true;
133
134     /* Allocate a thread to be the main thread */
135     liveThreads = the_main_thread = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
136     assert(the_main_thread != 0);
137         heap_addreference(&liveThreads);
138     
139     the_main_thread->PrivateInfo = 1;
140     CONTEXT(the_main_thread).free = false;
141
142     the_main_thread->name = javastring_new(unicode_new_char("main"));
143     the_main_thread->priority = NORM_THREAD_PRIO;
144     CONTEXT(the_main_thread).priority = (u1)the_main_thread->priority;
145     CONTEXT(the_main_thread).exceptionptr = 0;
146     the_main_thread->next = 0;
147     CONTEXT(the_main_thread).status = THREAD_SUSPENDED;
148     CONTEXT(the_main_thread).stackBase = CONTEXT(the_main_thread).stackEnd = stackbottom;
149     THREADINFO(&CONTEXT(the_main_thread));
150
151     DBG( printf("main thread %p base %p end %p\n", 
152                                 the_main_thread,
153                                 CONTEXT(the_main_thread).stackBase,
154                                 CONTEXT(the_main_thread).stackEnd); );
155
156         CONTEXT(the_main_thread).flags = THREAD_FLAGS_NOSTACKALLOC;
157         CONTEXT(the_main_thread).nextlive = 0;
158         the_main_thread->single_step = 0;
159         the_main_thread->daemon = 0;
160         the_main_thread->stillborn = 0;
161         the_main_thread->target = 0;
162         the_main_thread->interruptRequested = 0;
163         the_main_thread->group =
164                 (threadGroup*)builtin_new(loader_load(unicode_new_char("java/lang/ThreadGroup")));
165         /* we should call the constructor */
166         assert(the_main_thread->group != 0);
167
168         talive++;
169
170         /* Load exception classes */
171         class_java_lang_ThreadDeath = loader_load(unicode_new_char("java/lang/ThreadDeath"));
172
173         DBG( fprintf(stderr, "finishing initThreads\n"); );
174
175     mainThread = currentThread = the_main_thread;
176
177         /* Add thread into runQ */
178         iresumeThread(mainThread);
179
180         assert(blockInts == 0);
181 }
182
183 /*
184  * Start a new thread running.
185  */
186 void
187 startThread (thread* tid)
188 {
189     int i;
190
191     /* Allocate a stack context */
192     for (i = 0; i < MAXTHREADS; ++i)
193                 if (contexts[i].free)
194                         break;
195
196     if (i == MAXTHREADS)
197                 panic("Too many threads");
198
199     tid->PrivateInfo = i + 1;
200     CONTEXT(tid).free = false;
201     CONTEXT(tid).nextlive = liveThreads;
202     liveThreads = tid;
203     allocThreadStack(tid, threadStackSize);
204     CONTEXT(tid).usedStackTop = CONTEXT(tid).stackBase;
205     CONTEXT(tid).flags = THREAD_FLAGS_GENERAL;
206     CONTEXT(tid).status = THREAD_SUSPENDED;
207     CONTEXT(tid).priority = (u1)tid->priority;
208     CONTEXT(tid).exceptionptr = 0;
209
210     /* Construct the initial restore point. */
211     THREADINIT((&CONTEXT(tid)), firstStartThread);
212
213     DBG( printf("new thread %p base %p end %p\n",
214                                 tid, CONTEXT(tid).stackBase,
215                                 CONTEXT(tid).stackEnd); );
216
217         talive++;
218         if (tid->daemon)
219                 tdaemon++;
220
221         /* Add thread into runQ */
222         iresumeThread(tid);
223 }
224
225 /*
226  * Start a daemon thread.
227  */
228 static thread*
229 startDaemon(void* func, char* nm, int stackSize)
230 {
231     thread* tid;
232     int i;
233
234     DBG( printf("startDaemon %s\n", nm); );
235
236         tid = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
237         assert(tid != 0);
238
239         for (i = 0; i < MAXTHREADS; ++i)
240                 if (contexts[i].free)
241                         break;
242         if (i == MAXTHREADS)
243                 panic("Too many threads");
244
245         tid->PrivateInfo = i + 1;
246         CONTEXT(tid).free = false;
247         tid->name = 0;          /* for the moment */
248         tid->priority = MAX_THREAD_PRIO;
249         CONTEXT(tid).priority = (u1)tid->priority;
250         tid->next = 0;
251         CONTEXT(tid).status = THREAD_SUSPENDED;
252
253         allocThreadStack(tid, stackSize);
254         tid->single_step = 0;
255         tid->daemon = 1;
256         tid->stillborn = 0;
257         tid->target = 0;
258         tid->interruptRequested = 0;
259         tid->group = 0;
260
261         /* Construct the initial restore point. */
262         THREADINIT((&CONTEXT(tid)), func);
263
264         talive++;
265         tdaemon++;
266
267         return tid;
268 }
269
270 /*
271  * All threads start here.
272  */
273 static void
274 firstStartThread(void)
275 {
276     methodinfo *method;
277
278     DBG( printf("firstStartThread %p\n", currentThread); );
279
280         /* Every thread starts with the interrupts off */
281         intsRestore();
282         assert(blockInts == 0);
283
284         /* Find the run()V method and call it */
285         method = class_findmethod(currentThread->header.vftbl->class,
286                                                           unicode_new_char("run"), unicode_new_char("()V"));
287         if (method == 0)
288                 panic("Cannot find method \'void run ()\'");
289         asm_calljavamethod(method, currentThread, NULL, NULL, NULL);
290
291         killThread(0);
292         assert("Thread returned from killThread" == 0);
293 }
294
295 /*
296  * Resume a thread running.
297  * This routine has to be called only from locations which ensure
298  * run / block queue consistency. There is no check for illegal resume
299  * conditions (like explicitly resuming an IO blocked thread). There also
300  * is no update of any blocking queue. Both has to be done by the caller
301  */
302 void
303 iresumeThread(thread* tid)
304 {
305     DBG( printf("resumeThread %p\n", tid); );
306
307         intsDisable();
308
309         if (CONTEXT(tid).status != THREAD_RUNNING)
310         {
311                 CONTEXT(tid).status = THREAD_RUNNING;
312
313                 DBG( fprintf(stderr, "prio is %d\n", CONTEXT(tid).priority); );
314
315                 /* Place thread on the end of its queue */
316                 if (threadQhead[CONTEXT(tid).priority] == 0) {
317                         threadQhead[CONTEXT(tid).priority] = tid;
318                         threadQtail[CONTEXT(tid).priority] = tid;
319                         if (CONTEXT(tid).priority
320                                 > CONTEXT(currentThread).priority)
321                                 needReschedule = true;
322                 }
323                 else
324                 {
325                         threadQtail[CONTEXT(tid).priority]->next = tid;
326                         threadQtail[CONTEXT(tid).priority] = tid;
327                 }
328                 tid->next = 0;
329         }
330         SDBG( else { printf("Re-resuming %p\n", tid); } );
331
332         intsRestore();
333 }
334
335 /*
336  * Yield process to another thread of equal priority.
337  */
338 void
339 yieldThread()
340 {
341     intsDisable();
342
343     if (threadQhead[CONTEXT(currentThread).priority]
344                 != threadQtail[CONTEXT(currentThread).priority])
345     {
346                 /* Get the next thread and move me to the end */
347                 threadQhead[CONTEXT(currentThread).priority] = currentThread->next;
348                 threadQtail[CONTEXT(currentThread).priority]->next = currentThread;
349                 threadQtail[CONTEXT(currentThread).priority] = currentThread;
350                 currentThread->next = 0;
351                 needReschedule = true;
352     }
353
354     intsRestore();
355 }
356
357 /*
358  * Explicit request by user to resume a thread
359  * The definition says that it is just legal to call this after a preceeding
360  * suspend (which got through). If the thread was blocked for some other
361  * reason (either sleep or IO or a muxSem), we simply can't do it
362  * We use a new thread flag THREAD_FLAGS_USER_SUSPEND for this purpose
363  * (which is set by suspendThread(.))
364  */
365 void
366 resumeThread(thread* tid)
367 {
368     if ((CONTEXT(tid).flags & THREAD_FLAGS_USER_SUSPEND) != 0)
369     {
370                 intsDisable();
371                 CONTEXT(tid).flags &= ~THREAD_FLAGS_USER_SUSPEND;
372                 iresumeThread(tid);
373                 intsRestore();
374     }
375 }
376
377 /*
378  * Suspend a thread.
379  * This is an explicit user request to suspend the thread - the counterpart
380  * for resumeThreadRequest(.). It is JUST called by the java method
381  * Thread.suspend()
382  * What makes it distinct is the fact that the suspended thread is not contained
383  * in any block queue. Without a special flag (indicating the user suspend), we
384  * can't check s suspended thread for this condition afterwards (which is
385  * required by resumeThreadRequest()). The new thread flag
386  * THREAD_FLAGS_USER_SUSPEND is used for this purpose.
387  */
388 void
389 suspendThread(thread* tid)
390 {
391     thread** ntid;
392
393     intsDisable();
394
395     if (CONTEXT(tid).status != THREAD_SUSPENDED)
396     {
397                 CONTEXT(tid).status = THREAD_SUSPENDED;
398                 
399                 /*
400                  * This is used to indicate the explicit suspend condition
401                  * required by resumeThreadRequest()
402                  */
403                 CONTEXT(tid).flags |= THREAD_FLAGS_USER_SUSPEND;
404
405                 for (ntid = &threadQhead[CONTEXT(tid).priority];
406                          *ntid != 0;
407                          ntid = &(*ntid)->next)
408                 {
409                         if (*ntid == tid)
410                         {
411                                 *ntid = tid->next;
412                                 tid->next = 0;
413                                 if (tid == currentThread)
414                                 {
415                                         reschedule();
416                                 }
417                                 break;
418                         }
419                 }
420     }
421         SDBG( else { printf("Re-suspending %p\n", tid); } );
422
423         intsRestore();
424 }
425
426 /*
427  * Suspend a thread on a queue.
428  */
429 void
430 suspendOnQThread(thread* tid, thread** queue)
431 {
432     thread** ntid;
433
434         DBG( printf("suspendOnQThread %p %p\n", tid, queue); );
435
436         assert(blockInts == 1);
437
438         if (CONTEXT(tid).status != THREAD_SUSPENDED)
439         {
440                 CONTEXT(tid).status = THREAD_SUSPENDED;
441
442                 for (ntid = &threadQhead[CONTEXT(tid).priority];
443                          *ntid != 0;
444                          ntid = &(*ntid)->next)
445                 {
446                         if (*ntid == tid)
447                         {
448                                 *ntid = tid->next;
449                                 /* Insert onto head of lock wait Q */
450                                 tid->next = *queue;
451                                 *queue = tid;
452                                 if (tid == currentThread)
453                                 {
454                                         DBG( fprintf(stderr, "suspending %p (cur=%p) with prio %d\n",
455                                                                  tid, currentThread, CONTEXT(tid).priority); );
456                                         reschedule();
457                                 }
458                                 break;
459                         }
460                 }
461         }
462         SDBG( else { printf("Re-suspending %p on %p\n", tid, *queue); } );
463 }
464
465 /*
466  * Kill thread.
467  */
468 void
469 killThread(thread* tid)
470 {
471     thread** ntid;
472
473     intsDisable();
474
475     /* A null tid means the current thread */
476     if (tid == 0)
477     {
478                 tid = currentThread;
479     }
480
481         DBG( printf("killThread %p\n", tid); );
482
483         if (CONTEXT(tid).status != THREAD_DEAD)
484         {
485                 /* Get thread off runq (if it needs it) */
486                 if (CONTEXT(tid).status == THREAD_RUNNING)
487                 {
488                         for (ntid = &threadQhead[CONTEXT(tid).priority];
489                                  *ntid != 0;
490                                  ntid = &(*ntid)->next)
491                         {
492                                 if (*ntid == tid)
493                                 {
494                                         *ntid = tid->next;
495                                         break;
496                                 }
497                         }
498                 }
499
500                 CONTEXT(tid).status = THREAD_DEAD;
501                 talive--;
502                 if (tid->daemon) {
503                         tdaemon--;
504                 }
505
506                 /* If we only have daemons left, then everyone is dead. */
507                 if (talive == tdaemon) {
508                         /* Am I suppose to close things down nicely ?? */
509                         exit(0);
510                 }
511
512                 /* Notify on the object just in case anyone is waiting */
513                 internal_lock_mutex_for_object(&tid->header);
514                 internal_broadcast_cond_for_object(&tid->header);
515                 internal_unlock_mutex_for_object(&tid->header);
516
517                 /* Remove thread from live list to it can be garbaged */
518                 for (ntid = &liveThreads;
519                          *ntid != 0;
520                          ntid = &(CONTEXT((*ntid)).nextlive))
521                 {
522                         if (tid == (*ntid))
523                         {
524                                 (*ntid) = CONTEXT(tid).nextlive;
525                                 break;
526                         }
527                 }
528
529                 /* Free stack */
530                 freeThreadStack(tid);
531
532                 /* free context */
533                 CONTEXT(tid).free = true;
534
535                 /* Run something else */
536                 needReschedule = true;
537         }
538         intsRestore();
539 }
540
541 /*
542  * Change thread priority.
543  */
544 void
545 setPriorityThread(thread* tid, int prio)
546 {
547     thread** ntid;
548
549     if (tid->PrivateInfo == 0) {
550                 tid->priority = prio;
551                 return;
552     }
553
554     if (CONTEXT(tid).status == THREAD_SUSPENDED) {
555                 CONTEXT(tid).priority = (u8)prio;
556                 return;
557     }
558
559     intsDisable();
560
561     /* Remove from current thread list */
562     for (ntid = &threadQhead[CONTEXT(tid).priority]; *ntid != 0; ntid = &(*ntid)->next) {
563                 if (*ntid == tid) {
564                         *ntid = tid->next;
565                         break;
566                 }
567     }
568
569     /* Insert onto a new one */
570     tid->priority = prio;
571     CONTEXT(tid).priority = (u8)tid->priority;
572     if (threadQhead[prio] == 0) {
573                 threadQhead[prio] = tid;
574                 threadQtail[prio] = tid;
575                 if (prio > CONTEXT(currentThread).priority) {
576                         needReschedule = true;
577                 }
578     }
579     else {
580                 threadQtail[prio]->next = tid;
581                 threadQtail[prio] = tid;
582     }
583     tid->next = 0;
584
585     intsRestore();
586 }
587
588 /*
589  * Is this thread alive?
590  */
591 bool
592 aliveThread(thread* tid)
593 {
594     if (tid->PrivateInfo != 0 && CONTEXT(tid).status != THREAD_DEAD)
595                 return (true);
596     else
597                 return (false);
598 }
599
600 /*
601  * Reschedule the thread.
602  * Called whenever a change in the running thread is required.
603  */
604 void
605 reschedule(void)
606 {
607     int i;
608     thread* lastThread;
609     int b;
610     /*    sigset_t nsig; */
611
612     /* A reschedule in a non-blocked context is half way to hell */
613     assert(blockInts > 0);
614     b = blockInts;
615     
616     /* Check events - we may release a high priority thread */
617     /* Just check IO, no need for a reschedule call by checkEvents() */
618     needReschedule = false;
619     checkEvents(false);
620
621     for (;;)
622     {
623                 for (i = MAX_THREAD_PRIO; i >= MIN_THREAD_PRIO; i--)
624                 {
625                         if (threadQhead[i] != 0)
626                         {
627                                 DBG( fprintf(stderr, "found thread %p in head %d\n", threadQhead[i], i); );
628
629                                 if (threadQhead[i] != currentThread)
630                                 {
631                                         /* USEDSTACKTOP((CONTEXT(currentThread).usedStackTop)); */
632
633                                         lastThread = currentThread;
634                                         currentThread = threadQhead[i];
635
636                                         CONTEXT(currentThread).exceptionptr = exceptionptr;
637
638                                         THREADSWITCH((&CONTEXT(currentThread)),
639                                                                  (&CONTEXT(lastThread)));
640                                         blockInts = b;
641
642                                         exceptionptr = CONTEXT(currentThread).exceptionptr;
643
644                                         /* Alarm signal may be blocked - if so
645                                          * unblock it.
646                                          */
647                                         /*
648                                           if (alarmBlocked == true) {
649                                           alarmBlocked = false;
650                                           sigemptyset(&nsig);
651                                           sigaddset(&nsig, SIGALRM);
652                                           sigprocmask(SIG_UNBLOCK, &nsig, 0);
653                                           }
654                                         */
655
656                                         /* I might be dying */
657                                         if ((CONTEXT(lastThread).flags & THREAD_FLAGS_KILLED)
658                                                 != 0)
659                                         {
660                                                 CONTEXT(lastThread).flags &= ~THREAD_FLAGS_KILLED;
661                                                 exceptionptr = native_new_and_init(class_java_lang_ThreadDeath);
662                                         }
663                                 }
664                                 /* Now we kill the schedule and turn ints
665                                    back on */
666                                 needReschedule = false;
667                                 return;
668                         }
669                 }
670                 /* Nothing to run - wait for external event */
671                 DBG( fprintf(stderr, "nothing more to do\n"); );
672                 checkEvents(true);
673     }
674 }
675
676 #endif