Threads are now garbage collected correctly (hopefully).
[cacao.git] / src / threads / green / threads.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                 contexts[i].thread = NULL;
134                 heap_addreference((void**)&contexts[i].thread);
135         }
136
137     /* Allocate a thread to be the main thread */
138     liveThreads = the_main_thread = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
139     assert(the_main_thread != 0);
140         /* heap_addreference((void **) &liveThreads); */
141     
142     the_main_thread->PrivateInfo = 1;
143     CONTEXT(the_main_thread).free = false;
144
145     the_main_thread->name = javastring_new(unicode_new_char("main"));
146     the_main_thread->priority = NORM_THREAD_PRIO;
147     CONTEXT(the_main_thread).priority = (u1)the_main_thread->priority;
148     CONTEXT(the_main_thread).exceptionptr = 0;
149     the_main_thread->next = 0;
150     CONTEXT(the_main_thread).status = THREAD_SUSPENDED;
151     CONTEXT(the_main_thread).stackBase = CONTEXT(the_main_thread).stackEnd = stackbottom;
152     THREADINFO(&CONTEXT(the_main_thread));
153
154     DBG( printf("main thread %p base %p end %p\n", 
155                                 the_main_thread,
156                                 CONTEXT(the_main_thread).stackBase,
157                                 CONTEXT(the_main_thread).stackEnd); );
158
159         CONTEXT(the_main_thread).flags = THREAD_FLAGS_NOSTACKALLOC;
160         CONTEXT(the_main_thread).nextlive = 0;
161         CONTEXT(the_main_thread).thread = the_main_thread;
162         the_main_thread->single_step = 0;
163         the_main_thread->daemon = 0;
164         the_main_thread->stillborn = 0;
165         the_main_thread->target = 0;
166         the_main_thread->interruptRequested = 0;
167         the_main_thread->group =
168                 (threadGroup*)builtin_new(loader_load(unicode_new_char("java/lang/ThreadGroup")));
169         /* we should call the constructor */
170         assert(the_main_thread->group != 0);
171
172         talive++;
173
174         /* Load exception classes */
175         class_java_lang_ThreadDeath = loader_load(unicode_new_char("java/lang/ThreadDeath"));
176
177         DBG( fprintf(stderr, "finishing initThreads\n"); );
178
179     mainThread = currentThread = the_main_thread;
180
181         /* heap_addreference((void**)&mainThread); */
182
183         /* Add thread into runQ */
184         iresumeThread(mainThread);
185
186         assert(blockInts == 0);
187 }
188
189 /*
190  * Start a new thread running.
191  */
192 void
193 startThread (thread* tid)
194 {
195     int i;
196
197     /* Allocate a stack context */
198     for (i = 0; i < MAXTHREADS; ++i)
199                 if (contexts[i].free)
200                         break;
201
202     if (i == MAXTHREADS)
203                 panic("Too many threads");
204
205     tid->PrivateInfo = i + 1;
206     CONTEXT(tid).free = false;
207         CONTEXT(tid).thread = tid;
208     CONTEXT(tid).nextlive = liveThreads;
209     liveThreads = tid;
210     allocThreadStack(tid, threadStackSize);
211     CONTEXT(tid).usedStackTop = CONTEXT(tid).stackBase;
212     CONTEXT(tid).flags = THREAD_FLAGS_GENERAL;
213     CONTEXT(tid).status = THREAD_SUSPENDED;
214     CONTEXT(tid).priority = (u1)tid->priority;
215     CONTEXT(tid).exceptionptr = 0;
216
217     /* Construct the initial restore point. */
218     THREADINIT((&CONTEXT(tid)), firstStartThread);
219
220     DBG( printf("new thread %p base %p end %p\n",
221                                 tid, CONTEXT(tid).stackBase,
222                                 CONTEXT(tid).stackEnd); );
223
224         talive++;
225         if (tid->daemon)
226                 tdaemon++;
227
228         /* Add thread into runQ */
229         iresumeThread(tid);
230 }
231
232 /*
233  * Start a daemon thread.
234  */
235 static thread*
236 startDaemon(void* func, char* nm, int stackSize)
237 {
238     thread* tid;
239     int i;
240
241     DBG( printf("startDaemon %s\n", nm); );
242
243         tid = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
244         assert(tid != 0);
245
246         for (i = 0; i < MAXTHREADS; ++i)
247                 if (contexts[i].free)
248                         break;
249         if (i == MAXTHREADS)
250                 panic("Too many threads");
251
252         tid->PrivateInfo = i + 1;
253         CONTEXT(tid).free = false;
254         tid->name = 0;          /* for the moment */
255         tid->priority = MAX_THREAD_PRIO;
256         CONTEXT(tid).priority = (u1)tid->priority;
257         tid->next = 0;
258         CONTEXT(tid).status = THREAD_SUSPENDED;
259
260         allocThreadStack(tid, stackSize);
261         tid->single_step = 0;
262         tid->daemon = 1;
263         tid->stillborn = 0;
264         tid->target = 0;
265         tid->interruptRequested = 0;
266         tid->group = 0;
267
268         /* Construct the initial restore point. */
269         THREADINIT((&CONTEXT(tid)), func);
270
271         talive++;
272         tdaemon++;
273
274         return tid;
275 }
276
277 /*
278  * All threads start here.
279  */
280 static void
281 firstStartThread(void)
282 {
283     methodinfo *method;
284
285     DBG( printf("firstStartThread %p\n", currentThread); );
286
287         /* Every thread starts with the interrupts off */
288         intsRestore();
289         assert(blockInts == 0);
290
291         /* Find the run()V method and call it */
292         method = class_findmethod(currentThread->header.vftbl->class,
293                                                           unicode_new_char("run"), unicode_new_char("()V"));
294         if (method == 0)
295                 panic("Cannot find method \'void run ()\'");
296         asm_calljavamethod(method, currentThread, NULL, NULL, NULL);
297
298         killThread(0);
299         assert("Thread returned from killThread" == 0);
300 }
301
302 /*
303  * Resume a thread running.
304  * This routine has to be called only from locations which ensure
305  * run / block queue consistency. There is no check for illegal resume
306  * conditions (like explicitly resuming an IO blocked thread). There also
307  * is no update of any blocking queue. Both has to be done by the caller
308  */
309 void
310 iresumeThread(thread* tid)
311 {
312     DBG( printf("resumeThread %p\n", tid); );
313
314         intsDisable();
315
316         if (CONTEXT(tid).status != THREAD_RUNNING)
317         {
318                 CONTEXT(tid).status = THREAD_RUNNING;
319
320                 DBG( fprintf(stderr, "prio is %d\n", CONTEXT(tid).priority); );
321
322                 /* Place thread on the end of its queue */
323                 if (threadQhead[CONTEXT(tid).priority] == 0) {
324                         threadQhead[CONTEXT(tid).priority] = tid;
325                         threadQtail[CONTEXT(tid).priority] = tid;
326                         if (CONTEXT(tid).priority
327                                 > CONTEXT(currentThread).priority)
328                                 needReschedule = true;
329                 }
330                 else
331                 {
332                         threadQtail[CONTEXT(tid).priority]->next = tid;
333                         threadQtail[CONTEXT(tid).priority] = tid;
334                 }
335                 tid->next = 0;
336         }
337         SDBG( else { printf("Re-resuming %p\n", tid); } );
338
339         intsRestore();
340 }
341
342 /*
343  * Yield process to another thread of equal priority.
344  */
345 void
346 yieldThread()
347 {
348     intsDisable();
349
350     if (threadQhead[CONTEXT(currentThread).priority]
351                 != threadQtail[CONTEXT(currentThread).priority])
352     {
353                 /* Get the next thread and move me to the end */
354                 threadQhead[CONTEXT(currentThread).priority] = currentThread->next;
355                 threadQtail[CONTEXT(currentThread).priority]->next = currentThread;
356                 threadQtail[CONTEXT(currentThread).priority] = currentThread;
357                 currentThread->next = 0;
358                 needReschedule = true;
359     }
360
361     intsRestore();
362 }
363
364 /*
365  * Explicit request by user to resume a thread
366  * The definition says that it is just legal to call this after a preceeding
367  * suspend (which got through). If the thread was blocked for some other
368  * reason (either sleep or IO or a muxSem), we simply can't do it
369  * We use a new thread flag THREAD_FLAGS_USER_SUSPEND for this purpose
370  * (which is set by suspendThread(.))
371  */
372 void
373 resumeThread(thread* tid)
374 {
375     if ((CONTEXT(tid).flags & THREAD_FLAGS_USER_SUSPEND) != 0)
376     {
377                 intsDisable();
378                 CONTEXT(tid).flags &= ~THREAD_FLAGS_USER_SUSPEND;
379                 iresumeThread(tid);
380                 intsRestore();
381     }
382 }
383
384 /*
385  * Suspend a thread.
386  * This is an explicit user request to suspend the thread - the counterpart
387  * for resumeThreadRequest(.). It is JUST called by the java method
388  * Thread.suspend()
389  * What makes it distinct is the fact that the suspended thread is not contained
390  * in any block queue. Without a special flag (indicating the user suspend), we
391  * can't check s suspended thread for this condition afterwards (which is
392  * required by resumeThreadRequest()). The new thread flag
393  * THREAD_FLAGS_USER_SUSPEND is used for this purpose.
394  */
395 void
396 suspendThread(thread* tid)
397 {
398     thread** ntid;
399
400     intsDisable();
401
402     if (CONTEXT(tid).status != THREAD_SUSPENDED)
403     {
404                 CONTEXT(tid).status = THREAD_SUSPENDED;
405                 
406                 /*
407                  * This is used to indicate the explicit suspend condition
408                  * required by resumeThreadRequest()
409                  */
410                 CONTEXT(tid).flags |= THREAD_FLAGS_USER_SUSPEND;
411
412                 for (ntid = &threadQhead[CONTEXT(tid).priority];
413                          *ntid != 0;
414                          ntid = &(*ntid)->next)
415                 {
416                         if (*ntid == tid)
417                         {
418                                 *ntid = tid->next;
419                                 tid->next = 0;
420                                 if (tid == currentThread)
421                                 {
422                                         reschedule();
423                                 }
424                                 break;
425                         }
426                 }
427     }
428         SDBG( else { printf("Re-suspending %p\n", tid); } );
429
430         intsRestore();
431 }
432
433 /*
434  * Suspend a thread on a queue.
435  */
436 void
437 suspendOnQThread(thread* tid, thread** queue)
438 {
439     thread** ntid;
440
441         DBG( printf("suspendOnQThread %p %p\n", tid, queue); );
442
443         assert(blockInts == 1);
444
445         if (CONTEXT(tid).status != THREAD_SUSPENDED)
446         {
447                 CONTEXT(tid).status = THREAD_SUSPENDED;
448
449                 for (ntid = &threadQhead[CONTEXT(tid).priority];
450                          *ntid != 0;
451                          ntid = &(*ntid)->next)
452                 {
453                         if (*ntid == tid)
454                         {
455                                 *ntid = tid->next;
456                                 /* Insert onto head of lock wait Q */
457                                 tid->next = *queue;
458                                 *queue = tid;
459                                 if (tid == currentThread)
460                                 {
461                                         DBG( fprintf(stderr, "suspending %p (cur=%p) with prio %d\n",
462                                                                  tid, currentThread, CONTEXT(tid).priority); );
463                                         reschedule();
464                                 }
465                                 break;
466                         }
467                 }
468         }
469         SDBG( else { printf("Re-suspending %p on %p\n", tid, *queue); } );
470 }
471
472 /*
473  * Kill thread.
474  */
475 void
476 killThread(thread* tid)
477 {
478     thread** ntid;
479
480     intsDisable();
481
482     /* A null tid means the current thread */
483     if (tid == 0)
484     {
485                 tid = currentThread;
486     }
487
488         DBG( printf("killThread %p\n", tid); );
489
490         if (CONTEXT(tid).status != THREAD_DEAD)
491         {
492                 /* Get thread off runq (if it needs it) */
493                 if (CONTEXT(tid).status == THREAD_RUNNING)
494                 {
495                         for (ntid = &threadQhead[CONTEXT(tid).priority];
496                                  *ntid != 0;
497                                  ntid = &(*ntid)->next)
498                         {
499                                 if (*ntid == tid)
500                                 {
501                                         *ntid = tid->next;
502                                         break;
503                                 }
504                         }
505                 }
506
507                 CONTEXT(tid).status = THREAD_DEAD;
508                 talive--;
509                 if (tid->daemon) {
510                         tdaemon--;
511                 }
512
513                 /* If we only have daemons left, then everyone is dead. */
514                 if (talive == tdaemon) {
515                         /* atexit functions get called to clean things up */
516                         exit(0);
517                 }
518
519                 /* Notify on the object just in case anyone is waiting */
520                 internal_lock_mutex_for_object(&tid->header);
521                 internal_broadcast_cond_for_object(&tid->header);
522                 internal_unlock_mutex_for_object(&tid->header);
523
524                 /* Remove thread from live list to it can be garbaged */
525                 for (ntid = &liveThreads;
526                          *ntid != 0;
527                          ntid = &(CONTEXT((*ntid)).nextlive))
528                 {
529                         if (tid == (*ntid))
530                         {
531                                 (*ntid) = CONTEXT(tid).nextlive;
532                                 break;
533                         }
534                 }
535
536                 /* Free stack */
537                 freeThreadStack(tid);
538
539                 /* free context */
540                 if (tid != mainThread) {
541                         CONTEXT(tid).free = true;
542                         CONTEXT(tid).thread = NULL;
543                 }
544
545                 /* Run something else */
546                 needReschedule = true;
547         }
548         intsRestore();
549 }
550
551 /*
552  * Change thread priority.
553  */
554 void
555 setPriorityThread(thread* tid, int prio)
556 {
557     thread** ntid;
558
559     if (tid->PrivateInfo == 0) {
560                 tid->priority = prio;
561                 return;
562     }
563
564     if (CONTEXT(tid).status == THREAD_SUSPENDED) {
565                 CONTEXT(tid).priority = (u8)prio;
566                 return;
567     }
568
569     intsDisable();
570
571     /* Remove from current thread list */
572     for (ntid = &threadQhead[CONTEXT(tid).priority]; *ntid != 0; ntid = &(*ntid)->next) {
573                 if (*ntid == tid) {
574                         *ntid = tid->next;
575                         break;
576                 }
577     }
578
579     /* Insert onto a new one */
580     tid->priority = prio;
581     CONTEXT(tid).priority = (u8)tid->priority;
582     if (threadQhead[prio] == 0) {
583                 threadQhead[prio] = tid;
584                 threadQtail[prio] = tid;
585                 if (prio > CONTEXT(currentThread).priority) {
586                         needReschedule = true;
587                 }
588     }
589     else {
590                 threadQtail[prio]->next = tid;
591                 threadQtail[prio] = tid;
592     }
593     tid->next = 0;
594
595     intsRestore();
596 }
597
598 /*
599  * Is this thread alive?
600  */
601 bool
602 aliveThread(thread* tid)
603 {
604     if (tid->PrivateInfo != 0 && CONTEXT(tid).status != THREAD_DEAD)
605                 return (true);
606     else
607                 return (false);
608 }
609
610 /*
611  * Reschedule the thread.
612  * Called whenever a change in the running thread is required.
613  */
614 void
615 reschedule(void)
616 {
617     int i;
618     thread* lastThread;
619     int b;
620     /*    sigset_t nsig; */
621
622     /* A reschedule in a non-blocked context is half way to hell */
623     assert(blockInts > 0);
624     b = blockInts;
625     
626     /* Check events - we may release a high priority thread */
627     /* Just check IO, no need for a reschedule call by checkEvents() */
628     needReschedule = false;
629     checkEvents(false);
630
631     for (;;)
632     {
633                 for (i = MAX_THREAD_PRIO; i >= MIN_THREAD_PRIO; i--)
634                 {
635                         if (threadQhead[i] != 0)
636                         {
637                                 DBG( fprintf(stderr, "found thread %p in head %d\n", threadQhead[i], i); );
638
639                                 if (threadQhead[i] != currentThread)
640                                 {
641                                         /* USEDSTACKTOP((CONTEXT(currentThread).usedStackTop)); */
642
643                                         lastThread = currentThread;
644                                         currentThread = threadQhead[i];
645
646                                         CONTEXT(currentThread).exceptionptr = exceptionptr;
647
648                                         THREADSWITCH((&CONTEXT(currentThread)),
649                                                                  (&CONTEXT(lastThread)));
650                                         blockInts = b;
651
652                                         exceptionptr = CONTEXT(currentThread).exceptionptr;
653
654                                         /* Alarm signal may be blocked - if so
655                                          * unblock it.
656                                          */
657                                         /*
658                                           if (alarmBlocked == true) {
659                                           alarmBlocked = false;
660                                           sigemptyset(&nsig);
661                                           sigaddset(&nsig, SIGALRM);
662                                           sigprocmask(SIG_UNBLOCK, &nsig, 0);
663                                           }
664                                         */
665
666                                         /* I might be dying */
667                                         if ((CONTEXT(lastThread).flags & THREAD_FLAGS_KILLED)
668                                                 != 0)
669                                         {
670                                                 CONTEXT(lastThread).flags &= ~THREAD_FLAGS_KILLED;
671                                                 exceptionptr = native_new_and_init(class_java_lang_ThreadDeath);
672                                         }
673                                 }
674                                 /* Now we kill the schedule and turn ints
675                                    back on */
676                                 needReschedule = false;
677                                 return;
678                         }
679                 }
680                 /* Nothing to run - wait for external event */
681                 DBG( fprintf(stderr, "nothing more to do\n"); );
682                 checkEvents(true);
683     }
684 }
685
686 #endif