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