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.
16 #include <sys/types.h>
17 #include <sys/mman.h> /* for mprotect */
22 #include "sysdep/defines.h"
23 #include "sysdep/threads.h"
25 #include "../tables.h"
26 #include "../native.h"
27 #include "../loader.h"
28 #include "../builtin.h"
29 #include "../asmpart.h"
31 static classinfo *class_java_lang_ThreadDeath;
41 #if defined(USE_INTERNAL_THREADS)
43 thread* currentThread = NULL;
45 thread* threadQhead[MAX_THREAD_PRIO + 1];
46 thread* threadQtail[MAX_THREAD_PRIO + 1];
48 thread* liveThreads = NULL;
54 ctx contexts[MAXTHREADS];
56 /* Number of threads alive, also counting daemons */
59 /* Number of daemon threads alive */
62 static void firstStartThread(void);
64 void reschedule(void);
66 /* Setup default thread stack size - this can be overwritten if required */
67 int threadStackSize = THREADSTACKSIZE;
69 static thread* startDaemon(void* func, char* nm, int stackSize);
72 * Allocate the stack for a thread
75 allocThreadStack (thread *tid, int size)
77 int pageSize = getpagesize(),
79 unsigned long pageBegin;
81 CONTEXT(tid).stackMem = malloc(size + 2 * pageSize);
82 assert(CONTEXT(tid).stackMem != 0);
83 CONTEXT(tid).stackEnd = CONTEXT(tid).stackMem + size + 2 * pageSize;
85 pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
86 pageBegin = pageBegin - pageBegin % pageSize;
88 result = mprotect((void*)pageBegin, pageSize, PROT_NONE);
91 CONTEXT(tid).stackBase = (u1*)pageBegin + pageSize;
95 * Free the stack for a thread
98 freeThreadStack (thread *tid)
100 if (!(CONTEXT(tid).flags & THREAD_FLAGS_NOSTACKALLOC))
102 int pageSize = getpagesize(),
104 unsigned long pageBegin;
106 pageBegin = (unsigned long)(CONTEXT(tid).stackMem) + pageSize - 1;
107 pageBegin = pageBegin - pageBegin % pageSize;
109 result = mprotect((void*)pageBegin, pageSize,
110 PROT_READ | PROT_WRITE | PROT_EXEC);
113 free(CONTEXT(tid).stackMem);
115 CONTEXT(tid).stackMem = 0;
116 CONTEXT(tid).stackBase = 0;
117 CONTEXT(tid).stackEnd = 0;
121 * Initialize threads.
124 initThreads(u1 *stackbottom)
126 thread *the_main_thread;
131 for (i = 0; i < MAXTHREADS; ++i)
132 contexts[i].free = true;
134 /* Allocate a thread to be the main thread */
135 liveThreads = the_main_thread = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
136 assert(the_main_thread != 0);
137 heap_addreference(&liveThreads);
139 the_main_thread->PrivateInfo = 1;
140 CONTEXT(the_main_thread).free = false;
142 the_main_thread->name = javastring_new(unicode_new_char("main"));
143 the_main_thread->priority = NORM_THREAD_PRIO;
144 CONTEXT(the_main_thread).priority = (u1)the_main_thread->priority;
145 CONTEXT(the_main_thread).exceptionptr = 0;
146 the_main_thread->next = 0;
147 CONTEXT(the_main_thread).status = THREAD_SUSPENDED;
148 CONTEXT(the_main_thread).stackBase = CONTEXT(the_main_thread).stackEnd = stackbottom;
149 THREADINFO(&CONTEXT(the_main_thread));
151 DBG( printf("main thread %p base %p end %p\n",
153 CONTEXT(the_main_thread).stackBase,
154 CONTEXT(the_main_thread).stackEnd); );
156 CONTEXT(the_main_thread).flags = THREAD_FLAGS_NOSTACKALLOC;
157 CONTEXT(the_main_thread).nextlive = 0;
158 the_main_thread->single_step = 0;
159 the_main_thread->daemon = 0;
160 the_main_thread->stillborn = 0;
161 the_main_thread->target = 0;
162 the_main_thread->interruptRequested = 0;
163 the_main_thread->group =
164 (threadGroup*)builtin_new(loader_load(unicode_new_char("java/lang/ThreadGroup")));
165 /* we should call the constructor */
166 assert(the_main_thread->group != 0);
170 /* Load exception classes */
171 class_java_lang_ThreadDeath = loader_load(unicode_new_char("java/lang/ThreadDeath"));
173 DBG( fprintf(stderr, "finishing initThreads\n"); );
175 mainThread = currentThread = the_main_thread;
177 /* Add thread into runQ */
178 iresumeThread(mainThread);
180 assert(blockInts == 0);
184 * Start a new thread running.
187 startThread (thread* tid)
191 /* Allocate a stack context */
192 for (i = 0; i < MAXTHREADS; ++i)
193 if (contexts[i].free)
197 panic("Too many threads");
199 tid->PrivateInfo = i + 1;
200 CONTEXT(tid).free = false;
201 CONTEXT(tid).nextlive = liveThreads;
203 allocThreadStack(tid, threadStackSize);
204 CONTEXT(tid).usedStackTop = CONTEXT(tid).stackBase;
205 CONTEXT(tid).flags = THREAD_FLAGS_GENERAL;
206 CONTEXT(tid).status = THREAD_SUSPENDED;
207 CONTEXT(tid).priority = (u1)tid->priority;
208 CONTEXT(tid).exceptionptr = 0;
210 /* Construct the initial restore point. */
211 THREADINIT((&CONTEXT(tid)), firstStartThread);
213 DBG( printf("new thread %p base %p end %p\n",
214 tid, CONTEXT(tid).stackBase,
215 CONTEXT(tid).stackEnd); );
221 /* Add thread into runQ */
226 * Start a daemon thread.
229 startDaemon(void* func, char* nm, int stackSize)
234 DBG( printf("startDaemon %s\n", nm); );
236 tid = (thread*)builtin_new(loader_load(unicode_new_char("java/lang/Thread")));
239 for (i = 0; i < MAXTHREADS; ++i)
240 if (contexts[i].free)
243 panic("Too many threads");
245 tid->PrivateInfo = i + 1;
246 CONTEXT(tid).free = false;
247 tid->name = 0; /* for the moment */
248 tid->priority = MAX_THREAD_PRIO;
249 CONTEXT(tid).priority = (u1)tid->priority;
251 CONTEXT(tid).status = THREAD_SUSPENDED;
253 allocThreadStack(tid, stackSize);
254 tid->single_step = 0;
258 tid->interruptRequested = 0;
261 /* Construct the initial restore point. */
262 THREADINIT((&CONTEXT(tid)), func);
271 * All threads start here.
274 firstStartThread(void)
278 DBG( printf("firstStartThread %p\n", currentThread); );
280 /* Every thread starts with the interrupts off */
282 assert(blockInts == 0);
284 /* Find the run()V method and call it */
285 method = class_findmethod(currentThread->header.vftbl->class,
286 unicode_new_char("run"), unicode_new_char("()V"));
288 panic("Cannot find method \'void run ()\'");
289 asm_calljavamethod(method, currentThread, NULL, NULL, NULL);
292 assert("Thread returned from killThread" == 0);
296 * Resume a thread running.
297 * This routine has to be called only from locations which ensure
298 * run / block queue consistency. There is no check for illegal resume
299 * conditions (like explicitly resuming an IO blocked thread). There also
300 * is no update of any blocking queue. Both has to be done by the caller
303 iresumeThread(thread* tid)
305 DBG( printf("resumeThread %p\n", tid); );
309 if (CONTEXT(tid).status != THREAD_RUNNING)
311 CONTEXT(tid).status = THREAD_RUNNING;
313 DBG( fprintf(stderr, "prio is %d\n", CONTEXT(tid).priority); );
315 /* Place thread on the end of its queue */
316 if (threadQhead[CONTEXT(tid).priority] == 0) {
317 threadQhead[CONTEXT(tid).priority] = tid;
318 threadQtail[CONTEXT(tid).priority] = tid;
319 if (CONTEXT(tid).priority
320 > CONTEXT(currentThread).priority)
321 needReschedule = true;
325 threadQtail[CONTEXT(tid).priority]->next = tid;
326 threadQtail[CONTEXT(tid).priority] = tid;
330 SDBG( else { printf("Re-resuming %p\n", tid); } );
336 * Yield process to another thread of equal priority.
343 if (threadQhead[CONTEXT(currentThread).priority]
344 != threadQtail[CONTEXT(currentThread).priority])
346 /* Get the next thread and move me to the end */
347 threadQhead[CONTEXT(currentThread).priority] = currentThread->next;
348 threadQtail[CONTEXT(currentThread).priority]->next = currentThread;
349 threadQtail[CONTEXT(currentThread).priority] = currentThread;
350 currentThread->next = 0;
351 needReschedule = true;
358 * Explicit request by user to resume a thread
359 * The definition says that it is just legal to call this after a preceeding
360 * suspend (which got through). If the thread was blocked for some other
361 * reason (either sleep or IO or a muxSem), we simply can't do it
362 * We use a new thread flag THREAD_FLAGS_USER_SUSPEND for this purpose
363 * (which is set by suspendThread(.))
366 resumeThread(thread* tid)
368 if ((CONTEXT(tid).flags & THREAD_FLAGS_USER_SUSPEND) != 0)
371 CONTEXT(tid).flags &= ~THREAD_FLAGS_USER_SUSPEND;
379 * This is an explicit user request to suspend the thread - the counterpart
380 * for resumeThreadRequest(.). It is JUST called by the java method
382 * What makes it distinct is the fact that the suspended thread is not contained
383 * in any block queue. Without a special flag (indicating the user suspend), we
384 * can't check s suspended thread for this condition afterwards (which is
385 * required by resumeThreadRequest()). The new thread flag
386 * THREAD_FLAGS_USER_SUSPEND is used for this purpose.
389 suspendThread(thread* tid)
395 if (CONTEXT(tid).status != THREAD_SUSPENDED)
397 CONTEXT(tid).status = THREAD_SUSPENDED;
400 * This is used to indicate the explicit suspend condition
401 * required by resumeThreadRequest()
403 CONTEXT(tid).flags |= THREAD_FLAGS_USER_SUSPEND;
405 for (ntid = &threadQhead[CONTEXT(tid).priority];
407 ntid = &(*ntid)->next)
413 if (tid == currentThread)
421 SDBG( else { printf("Re-suspending %p\n", tid); } );
427 * Suspend a thread on a queue.
430 suspendOnQThread(thread* tid, thread** queue)
434 DBG( printf("suspendOnQThread %p %p\n", tid, queue); );
436 assert(blockInts == 1);
438 if (CONTEXT(tid).status != THREAD_SUSPENDED)
440 CONTEXT(tid).status = THREAD_SUSPENDED;
442 for (ntid = &threadQhead[CONTEXT(tid).priority];
444 ntid = &(*ntid)->next)
449 /* Insert onto head of lock wait Q */
452 if (tid == currentThread)
454 DBG( fprintf(stderr, "suspending %p (cur=%p) with prio %d\n",
455 tid, currentThread, CONTEXT(tid).priority); );
462 SDBG( else { printf("Re-suspending %p on %p\n", tid, *queue); } );
469 killThread(thread* tid)
475 /* A null tid means the current thread */
481 DBG( printf("killThread %p\n", tid); );
483 if (CONTEXT(tid).status != THREAD_DEAD)
485 /* Get thread off runq (if it needs it) */
486 if (CONTEXT(tid).status == THREAD_RUNNING)
488 for (ntid = &threadQhead[CONTEXT(tid).priority];
490 ntid = &(*ntid)->next)
500 CONTEXT(tid).status = THREAD_DEAD;
506 /* If we only have daemons left, then everyone is dead. */
507 if (talive == tdaemon) {
508 /* Am I suppose to close things down nicely ?? */
512 /* Notify on the object just in case anyone is waiting */
513 internal_lock_mutex_for_object(&tid->header);
514 internal_broadcast_cond_for_object(&tid->header);
515 internal_unlock_mutex_for_object(&tid->header);
517 /* Remove thread from live list to it can be garbaged */
518 for (ntid = &liveThreads;
520 ntid = &(CONTEXT((*ntid)).nextlive))
524 (*ntid) = CONTEXT(tid).nextlive;
530 freeThreadStack(tid);
533 CONTEXT(tid).free = true;
535 /* Run something else */
536 needReschedule = true;
542 * Change thread priority.
545 setPriorityThread(thread* tid, int prio)
549 if (tid->PrivateInfo == 0) {
550 tid->priority = prio;
554 if (CONTEXT(tid).status == THREAD_SUSPENDED) {
555 CONTEXT(tid).priority = (u8)prio;
561 /* Remove from current thread list */
562 for (ntid = &threadQhead[CONTEXT(tid).priority]; *ntid != 0; ntid = &(*ntid)->next) {
569 /* Insert onto a new one */
570 tid->priority = prio;
571 CONTEXT(tid).priority = (u8)tid->priority;
572 if (threadQhead[prio] == 0) {
573 threadQhead[prio] = tid;
574 threadQtail[prio] = tid;
575 if (prio > CONTEXT(currentThread).priority) {
576 needReschedule = true;
580 threadQtail[prio]->next = tid;
581 threadQtail[prio] = tid;
589 * Is this thread alive?
592 aliveThread(thread* tid)
594 if (tid->PrivateInfo != 0 && CONTEXT(tid).status != THREAD_DEAD)
601 * Reschedule the thread.
602 * Called whenever a change in the running thread is required.
612 /* A reschedule in a non-blocked context is half way to hell */
613 assert(blockInts > 0);
616 /* Check events - we may release a high priority thread */
617 /* Just check IO, no need for a reschedule call by checkEvents() */
618 needReschedule = false;
623 for (i = MAX_THREAD_PRIO; i >= MIN_THREAD_PRIO; i--)
625 if (threadQhead[i] != 0)
627 DBG( fprintf(stderr, "found thread %p in head %d\n", threadQhead[i], i); );
629 if (threadQhead[i] != currentThread)
631 /* USEDSTACKTOP((CONTEXT(currentThread).usedStackTop)); */
633 lastThread = currentThread;
634 currentThread = threadQhead[i];
636 CONTEXT(currentThread).exceptionptr = exceptionptr;
638 THREADSWITCH((&CONTEXT(currentThread)),
639 (&CONTEXT(lastThread)));
642 exceptionptr = CONTEXT(currentThread).exceptionptr;
644 /* Alarm signal may be blocked - if so
648 if (alarmBlocked == true) {
649 alarmBlocked = false;
651 sigaddset(&nsig, SIGALRM);
652 sigprocmask(SIG_UNBLOCK, &nsig, 0);
656 /* I might be dying */
657 if ((CONTEXT(lastThread).flags & THREAD_FLAGS_KILLED)
660 CONTEXT(lastThread).flags &= ~THREAD_FLAGS_KILLED;
661 exceptionptr = native_new_and_init(class_java_lang_ThreadDeath);
664 /* Now we kill the schedule and turn ints
666 needReschedule = false;
670 /* Nothing to run - wait for external event */
671 DBG( fprintf(stderr, "nothing more to do\n"); );