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