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