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