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