1 /* -*- mode: c; tab-width: 4; c-basic-offset: 4 -*- */
6 * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
15 #include <sys/types.h>
16 #include <sys/mman.h> /* for mprotect */
30 #include "toolbox/loging.h"
31 #include "toolbox/memory.h"
34 static classinfo *class_java_lang_ThreadDeath;
36 thread* currentThread = NULL;
38 thread* threadQhead[MAX_THREAD_PRIO + 1];
39 thread* threadQtail[MAX_THREAD_PRIO + 1];
41 thread* liveThreads = NULL;
42 thread* sleepThreads = NULL;
47 ctx contexts[MAXTHREADS];
49 /* Number of threads alive, also counting daemons */
52 /* Number of daemon threads alive */
55 static void firstStartThread(void);
57 void reschedule(void);
59 /* Setup default thread stack size - this can be overwritten if required */
60 int threadStackSize = THREADSTACKSIZE;
62 /* Pointer to the stack of the last killed thread. The free is delayed. */
63 void *stack_to_be_freed = 0;
65 static thread* startDaemon(void* func, char* nm, int stackSize);
68 * Allocate the stack for a thread
71 allocThreadStack (thread *tid, int size)
73 int pageSize = getpagesize();
74 unsigned long pageBegin;
76 assert(stack_to_be_freed == 0);
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;
82 pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
83 pageBegin = pageBegin - pageBegin % pageSize;
85 CONTEXT(tid).stackBase = (u1*)pageBegin + pageSize;
89 * Mark the stack for a thread to be freed. We cannot free the stack
90 * immediately because it is still in use!
93 freeThreadStack (thread *tid)
95 if (!(CONTEXT(tid).flags & THREAD_FLAGS_NOSTACKALLOC))
97 int pageSize = getpagesize();
98 unsigned long pageBegin;
100 pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
101 pageBegin = pageBegin - pageBegin % pageSize;
103 assert(stack_to_be_freed == 0);
105 stack_to_be_freed = CONTEXT(tid).stackMem;
107 CONTEXT(tid).stackMem = 0;
108 CONTEXT(tid).stackBase = 0;
109 CONTEXT(tid).stackEnd = 0;
113 * Initialize threads.
116 initThreads(u1 *stackbottom)
118 thread *the_main_thread;
120 char mainname[] = "main";
121 int len = strlen(mainname);
123 signal(SIGPIPE, SIG_IGN);
127 for (i = 0; i < MAXTHREADS; ++i) {
128 contexts[i].free = true;
129 contexts[i].thread = NULL;
130 heap_addreference((void**)&contexts[i].thread);
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); */
138 the_main_thread->PrivateInfo = 1;
139 CONTEXT(the_main_thread).free = false;
145 m = class_findmethod(
146 class_java_lang_String,
147 utf_new_char ("toCharArray"),
148 utf_new_char ("()[C")
150 printf("DEADCODE LIVES ?????????\n");fflush(stdout);
151 the_main_thread->name = asm_calljavafunction (m, javastring_new(utf_new_char("main")), 0, 0, 0);
154 the_main_thread->name = builtin_newarray_char(len);
155 { u2 *d = the_main_thread->name->data;
156 for (i=0; i<len; i++)
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));
167 DBG( printf("main thread %p base %p end %p\n",
169 CONTEXT(the_main_thread).stackBase,
170 CONTEXT(the_main_thread).stackEnd); );
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;
180 the_main_thread->contextClassLoader = 0;
181 the_main_thread->inheritedAccessControlContext = 0;
182 the_main_thread->values = 0;
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);
190 /* Load exception classes */
191 class_java_lang_ThreadDeath = loader_load(utf_new_char("java/lang/ThreadDeath"));
193 DBG( fprintf(stderr, "finishing initThreads\n"); );
195 mainThread = currentThread = the_main_thread;
197 /* heap_addreference((void**)&mainThread); */
199 /* Add thread into runQ */
200 iresumeThread(mainThread);
202 assert(blockInts == 0);
206 * Start a new thread running.
209 startThread (thread* tid)
213 /* Allocate a stack context */
214 for (i = 0; i < MAXTHREADS; ++i)
215 if (contexts[i].free)
219 panic("Too many threads");
221 assert(tid->priority >= MIN_THREAD_PRIO && tid->priority <= MAX_THREAD_PRIO);
223 tid->PrivateInfo = i + 1;
224 CONTEXT(tid).free = false;
225 CONTEXT(tid).thread = tid;
226 CONTEXT(tid).nextlive = liveThreads;
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;
235 /* Construct the initial restore point. */
236 THREADINIT((&CONTEXT(tid)), firstStartThread);
238 DBG( printf("new thread %p base %p end %p\n",
239 tid, CONTEXT(tid).stackBase,
240 CONTEXT(tid).stackEnd); );
246 /* Add thread into runQ */
251 * Start a daemon thread.
254 startDaemon(void* func, char* nm, int stackSize)
259 DBG( printf("startDaemon %s\n", nm); );
261 tid = (thread*)builtin_new(loader_load(utf_new_char("java/lang/Thread")));
264 for (i = 0; i < MAXTHREADS; ++i)
265 if (contexts[i].free)
268 panic("Too many threads");
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;
276 CONTEXT(tid).status = THREAD_SUSPENDED;
278 allocThreadStack(tid, stackSize);
279 tid->single_step = 0;
285 /* Construct the initial restore point. */
286 THREADINIT((&CONTEXT(tid)), func);
295 * All threads start here.
298 firstStartThread(void)
301 java_objectheader *local_exceptionptr = NULL;
303 DBG( printf("firstStartThread %p\n", currentThread); );
305 if (stack_to_be_freed != 0) {
306 stack_to_be_freed = 0;
309 /* Every thread starts with the interrupts off */
311 assert(blockInts == 0);
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"));
317 panic("Cannot find method \'void run ()\'");
319 local_exceptionptr = asm_calljavamethod(method, currentThread, NULL, NULL, NULL);
321 if (local_exceptionptr) {
322 utf_display(local_exceptionptr->vftbl->class->name);
327 assert("Thread returned from killThread" == 0);
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
338 iresumeThread(thread* tid)
340 DBG( printf("resumeThread %p\n", tid); );
344 if (CONTEXT(tid).status != THREAD_RUNNING)
346 CONTEXT(tid).status = THREAD_RUNNING;
348 DBG( fprintf(stderr, "prio is %d\n", CONTEXT(tid).priority); );
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;
360 threadQtail[CONTEXT(tid).priority]->next = tid;
361 threadQtail[CONTEXT(tid).priority] = tid;
365 SDBG( else { printf("Re-resuming %p\n", tid); } );
371 * Yield process to another thread of equal priority.
378 if (threadQhead[CONTEXT(currentThread).priority]
379 != threadQtail[CONTEXT(currentThread).priority])
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;
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(.))
401 resumeThread (thread* tid)
403 if ((CONTEXT(tid).flags & THREAD_FLAGS_USER_SUSPEND) != 0)
406 CONTEXT(tid).flags &= ~THREAD_FLAGS_USER_SUSPEND;
414 * This is an explicit user request to suspend the thread - the counterpart
415 * for resumeThreadRequest(.). It is JUST called by the java method
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.
424 suspendThread(thread* tid)
430 if (CONTEXT(tid).status != THREAD_SUSPENDED)
432 CONTEXT(tid).status = THREAD_SUSPENDED;
435 * This is used to indicate the explicit suspend condition
436 * required by resumeThreadRequest()
438 CONTEXT(tid).flags |= THREAD_FLAGS_USER_SUSPEND;
440 for (ntid = &threadQhead[CONTEXT(tid).priority];
442 ntid = &(*ntid)->next)
448 if (tid == currentThread)
456 SDBG( else { printf("Re-suspending %p\n", tid); } );
462 * Suspend a thread on a queue.
465 suspendOnQThread(thread* tid, thread** queue)
469 DBG( printf("suspendOnQThread %p %p\n", tid, queue); );
471 assert(blockInts > 0);
473 if (CONTEXT(tid).status != THREAD_SUSPENDED)
475 CONTEXT(tid).status = THREAD_SUSPENDED;
477 for (ntid = &threadQhead[CONTEXT(tid).priority];
479 ntid = &(*ntid)->next)
484 /* Insert onto head of lock wait Q */
487 if (tid == currentThread)
489 DBG( fprintf(stderr, "suspending %p (cur=%p) with prio %d\n",
490 tid, currentThread, CONTEXT(tid).priority); );
497 SDBG( else { printf("Re-suspending %p on %p\n", tid, *queue); } );
504 killThread(thread* tid)
510 /* A null tid means the current thread */
516 DBG( printf("killThread %p\n", tid); );
518 if (CONTEXT(tid).status != THREAD_DEAD)
520 /* Get thread off runq (if it needs it) */
521 if (CONTEXT(tid).status == THREAD_RUNNING)
523 for (ntid = &threadQhead[CONTEXT(tid).priority];
525 ntid = &(*ntid)->next)
535 CONTEXT(tid).status = THREAD_DEAD;
541 /* If we only have daemons left, then everyone is dead. */
542 if (talive == tdaemon) {
543 /* atexit functions get called to clean things up */
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);
553 /* Remove thread from live list to it can be garbaged */
554 for (ntid = &liveThreads;
556 ntid = &(CONTEXT((*ntid)).nextlive))
560 (*ntid) = CONTEXT(tid).nextlive;
566 freeThreadStack(tid);
569 if (tid != mainThread) {
570 CONTEXT(tid).free = true;
571 CONTEXT(tid).thread = NULL;
574 /* Run something else */
575 needReschedule = true;
581 * Change thread priority.
584 setPriorityThread(thread* tid, int prio)
588 assert(prio >= MIN_THREAD_PRIO && prio <= MAX_THREAD_PRIO);
590 if (tid->PrivateInfo == 0) {
591 tid->priority = prio;
595 if (CONTEXT(tid).status == THREAD_SUSPENDED) {
596 CONTEXT(tid).priority = (u8)prio;
602 /* Remove from current thread list */
603 for (ntid = &threadQhead[CONTEXT(tid).priority]; *ntid != 0; ntid = &(*ntid)->next) {
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;
621 threadQtail[prio]->next = tid;
622 threadQtail[prio] = tid;
630 * Get the current time in milliseconds since 1970-01-01.
638 gettimeofday(&tv, 0);
642 time += tv.tv_usec / 1000;
648 * Put a thread to sleep.
651 sleepThread (s8 time)
655 /* Sleep for no time */
662 /* Get absolute time */
663 CONTEXT(currentThread).time = time + currentTime();
665 /* Find place in alarm list */
666 for (tidp = &sleepThreads; (*tidp) != 0; tidp = &(*tidp)->next)
668 if (CONTEXT(*tidp).time > CONTEXT(currentThread).time)
672 /* Suspend thread on it */
673 suspendOnQThread(currentThread, tidp);
679 * Is this thread alive?
682 aliveThread(thread* tid)
684 if (tid->PrivateInfo != 0 && CONTEXT(tid).status != THREAD_DEAD)
691 * Reschedule the thread.
692 * Called whenever a change in the running thread is required.
702 /* A reschedule in a non-blocked context is half way to hell */
703 assert(blockInts > 0);
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;
713 for (i = MAX_THREAD_PRIO; i >= MIN_THREAD_PRIO; i--)
715 if (threadQhead[i] != 0)
717 DBG( fprintf(stderr, "found thread %p in head %d\n", threadQhead[i], i); );
719 if (threadQhead[i] != currentThread)
721 /* USEDSTACKTOP((CONTEXT(currentThread).usedStackTop)); */
723 lastThread = currentThread;
724 currentThread = threadQhead[i];
726 CONTEXT(currentThread).exceptionptr = exceptionptr;
728 DBG( fprintf(stderr, "thread switch from: %p to: %p\n", lastThread, currentThread); );
729 THREADSWITCH((&CONTEXT(currentThread)),
730 (&CONTEXT(lastThread)));
733 exceptionptr = CONTEXT(currentThread).exceptionptr;
735 if (stack_to_be_freed != 0) {
736 stack_to_be_freed = 0;
739 /* Alarm signal may be blocked - if so
743 if (alarmBlocked == true) {
744 alarmBlocked = false;
746 sigaddset(&nsig, SIGALRM);
747 sigprocmask(SIG_UNBLOCK, &nsig, 0);
751 /* I might be dying */
752 if ((CONTEXT(lastThread).flags & THREAD_FLAGS_KILLED)
755 CONTEXT(lastThread).flags &= ~THREAD_FLAGS_KILLED;
756 exceptionptr = native_new_and_init(class_java_lang_ThreadDeath);
759 /* Now we kill the schedule and turn ints
761 needReschedule = false;
765 /* Nothing to run - wait for external event */
766 DBG( fprintf(stderr, "nothing more to do\n"); );