home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
kaffe-0.5p4-src.tgz
/
tar.out
/
contrib
/
kaffe
/
kaffevm
/
thread.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-09-28
|
13KB
|
598 lines
/*
* thread.c
* Thread support.
*
* Copyright (c) 1996 Systems Architecture Research Centre,
* City University, London, UK.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* Written by Tim Wilkinson <tim@sarc.city.ac.uk>, February 1996.
*/
#define DBG(s)
#define SDBG(s)
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#include <signal.h>
#include "gtypes.h"
#include "access.h"
#include "object.h"
#include "constants.h"
#include "classMethod.h"
#include "baseClasses.h"
#include "thread.h"
#include "locks.h"
#include "exception.h"
#include "support.h"
#include "md.h"
#if defined(USE_INTERNAL_THREADS)
#if defined(INTERPRETER)
#include <setjmp.h>
extern jmp_buf* cjbuf;
#endif
thread* currentThread;
thread* threadQhead[MAX_THREAD_PRIO + 1];
thread* threadQtail[MAX_THREAD_PRIO + 1];
thread* garbageman;
thread* finalman;
thread* liveThreads;
thread* alarmList;
classes* ThreadClass;
int blockInts;
bool needReschedule;
static int talive;
static int tdaemon;
static void firstStartThread(void);
static thread* startDaemon(void*, char*);
static void alarmException(int);
void reschedule(void);
void finaliserMan(void);
void gcMan(void);
void checkEvents(bool);
/* Setup default thread stack size - this can be overwritten if required */
int threadStackSize = THREADSTACKSIZE;
#define ALLOCTHREADSTACK(_t) \
(_t)->PrivateInfo->stackBase = malloc(threadStackSize); \
assert((_t)->PrivateInfo->stackBase != 0); \
(_t)->PrivateInfo->stackEnd = (_t)->PrivateInfo->stackBase + \
threadStackSize
#define FREETHREADSTACK(_t) \
if (!((_t)->PrivateInfo->flags & THREAD_FLAGS_NOSTACKALLOC)) { \
free((_t)->PrivateInfo->stackBase); \
} \
(_t)->PrivateInfo->stackBase = 0; \
(_t)->PrivateInfo->stackEnd = 0
/*
* Initialise threads.
*/
void
initThreads(void)
{
/* Get a handle on the thread class */
ThreadClass = lookupClass(addString(THREADCLASS));
assert(ThreadClass != 0);
/* Allocate a thread to be the main thread */
currentThread = (thread*)alloc_object(ThreadClass, false);
assert(currentThread != 0);
currentThread->PrivateInfo = calloc(sizeof(ctx), 1);
liveThreads = currentThread;
currentThread->name = makeJavaCharArray("main", strlen("main"));
currentThread->priority = NORM_THREAD_PRIO;
currentThread->PrivateInfo->priority = (uint8)currentThread->priority;
currentThread->next = 0;
currentThread->PrivateInfo->status = THREAD_SUSPENDED;
assert(currentThread->PrivateInfo != 0);
THREADINFO(currentThread->PrivateInfo);
DBG( printf("main thread %x base %x end %x\n", currentThread, currentThread->PrivateInfo->stackBase, currentThread->PrivateInfo->stackEnd); )
currentThread->PrivateInfo->flags = THREAD_FLAGS_NOSTACKALLOC;
currentThread->PrivateInfo->nextlive = 0;
currentThread->single_step = 0;
currentThread->daemon = 0;
currentThread->stillborn = 0;
currentThread->target = 0;
currentThread->interruptRequested = 0;
currentThread->group = (threadGroup*)execute_java_constructor(0, "java.lang.ThreadGroup", 0, "()V");
assert(currentThread->group != 0);
talive++;
/* Add thread into runQ */
resumeThread(currentThread);
/* Start any daemons we need */
finalman = startDaemon(&finaliserMan, "finaliser");
garbageman = startDaemon(&gcMan, "gc");
resumeThread(finalman);
resumeThread(garbageman);
/* Plug in the alarm handler */
#if defined(SIGALRM)
signal(SIGALRM, (SIG_T)alarmException);
#else
#error "No alarm signal support"
#endif
}
/*
* Start a new thread running.
*/
void
startThread(thread* tid)
{
/* Allocate a stack context */
assert(tid->PrivateInfo == 0);
tid->PrivateInfo = calloc(sizeof(ctx), 1);
assert(tid->PrivateInfo != 0);
tid->PrivateInfo->nextlive = liveThreads;
liveThreads = tid;
ALLOCTHREADSTACK(tid);
tid->PrivateInfo->flags = THREAD_FLAGS_GENERAL;
tid->PrivateInfo->status = THREAD_SUSPENDED;
tid->PrivateInfo->priority = (uint8)tid->priority;
/* Construct the initial restore point. */
THREADINIT(tid->PrivateInfo, &firstStartThread);
DBG( printf("new thread %x base %x end %x\n", tid, tid->PrivateInfo->stackBase, tid->PrivateInfo->stackEnd); )
talive++;
if (tid->daemon) {
tdaemon++;
}
/* Add thread into runQ */
resumeThread(tid);
}
/*
* All threads start here.
*/
static
void
firstStartThread(void)
{
DBG( printf("firstStartThread %x\n", currentThread); )
/* Every thread starts with the interrupts off */
intsRestore();
/* Find the run()V method and call it */
do_execute_java_method(0, (object*)currentThread, "run", "()V", 0, 0);
do_execute_java_method(0, (object*)currentThread, "exit", "()V", 0, 0);
killThread(0);
assert("Thread returned from killThread" == 0);
}
/*
* Resume a thread running.
*/
void
resumeThread(thread* tid)
{
DBG( printf("resumeThread %x\n", tid); )
intsDisable();
if (tid->PrivateInfo->status != THREAD_RUNNING) {
tid->PrivateInfo->status = THREAD_RUNNING;
/* Place thread on the end of its queue */
if (threadQhead[tid->PrivateInfo->priority] == 0) {
threadQhead[tid->PrivateInfo->priority] = tid;
threadQtail[tid->PrivateInfo->priority] = tid;
if (tid->PrivateInfo->priority > currentThread->PrivateInfo->priority) {
needReschedule = true;
}
}
else {
threadQtail[tid->PrivateInfo->priority]->next = tid;
threadQtail[tid->PrivateInfo->priority] = tid;
}
tid->next = 0;
}
SDBG( else {
printf("Re-resuming 0x%x\n", tid);
} )
intsRestore();
}
/*
* Yield process to another thread of equal priority.
*/
void
yieldThread()
{
intsDisable();
if (threadQhead[currentThread->PrivateInfo->priority] != threadQtail[currentThread->PrivateInfo->priority]) {
/* Get the next thread and move me to the end */
threadQhead[currentThread->PrivateInfo->priority] = currentThread->next;
threadQtail[currentThread->PrivateInfo->priority]->next = currentThread;
threadQtail[currentThread->PrivateInfo->priority] = currentThread;
currentThread->next = 0;
needReschedule = true;
}
intsRestore();
}
/*
* Suspend a thread.
*/
void
suspendThread(thread* tid)
{
thread** ntid;
intsDisable();
if (tid->PrivateInfo->status != THREAD_SUSPENDED) {
tid->PrivateInfo->status = THREAD_SUSPENDED;
for (ntid = &threadQhead[tid->PrivateInfo->priority]; *ntid != 0; ntid = &(*ntid)->next) {
if (*ntid == tid) {
*ntid = tid->next;
tid->next = 0;
if (tid == currentThread) {
reschedule();
}
break;
}
}
}
SDBG( else {
printf("Re-suspending 0x%x\n", tid);
} )
intsRestore();
}
/*
* Suspend a thread on a queue.
*/
void
suspendOnQThread(thread* tid, thread** queue)
{
thread** ntid;
DBG( printf("suspendOnQThread %x\n", tid); )
assert(blockInts == 1);
if (tid->PrivateInfo->status != THREAD_SUSPENDED) {
tid->PrivateInfo->status = THREAD_SUSPENDED;
for (ntid = &threadQhead[tid->PrivateInfo->priority]; *ntid != 0; ntid = &(*ntid)->next) {
if (*ntid == tid) {
*ntid = tid->next;
/* Insert onto head of lock wait Q */
tid->next = *queue;
*queue = tid;
if (tid == currentThread) {
reschedule();
}
break;
}
}
}
SDBG( else {
printf("Re-suspending 0x%x on %x\n", tid, *queue);
} )
}
/*
* Kill thread.
*/
void
killThread(thread* tid)
{
thread** ntid;
intsDisable();
/* A null tid means the current thread */
if (tid == 0) {
tid = currentThread;
}
DBG( printf("killThread %x\n", tid); )
if (tid->PrivateInfo->status != THREAD_DEAD) {
/* Get thread off runq (if it needs it) */
if (tid->PrivateInfo->status == THREAD_RUNNING) {
for (ntid = &threadQhead[tid->PrivateInfo->priority]; *ntid != 0; ntid = &(*ntid)->next) {
if (*ntid == tid) {
*ntid = tid->next;
break;
}
}
}
tid->PrivateInfo->status = THREAD_DEAD;
talive--;
if (tid->daemon) {
tdaemon--;
}
/* If we only have daemons left, then everyone is dead. */
if (talive == tdaemon) {
/* Am I suppose to close things down nicely ?? */
exit(0);
}
/* Notify on the object just in case anyone is waiting */
lockMutex(&tid->obj);
broadcastCond(&tid->obj);
unlockMutex(&tid->obj);
/* Remove thread from live list to it can be garbaged */
for (ntid = &liveThreads; *ntid != 0; ntid = &(*ntid)->PrivateInfo->nextlive) {
if (tid == (*ntid)) {
(*ntid) = tid->PrivateInfo->nextlive;
break;
}
}
/* Free stack */
FREETHREADSTACK(tid);
/* Run something else */
needReschedule = true;
}
intsRestore();
}
/*
* Change thread priority.
*/
void
setPriorityThread(thread* tid, int prio)
{
thread** ntid;
if (tid->PrivateInfo == 0) {
tid->priority = prio;
return;
}
if (tid->PrivateInfo->status == THREAD_SUSPENDED) {
tid->PrivateInfo->priority = (uint8)prio;
return;
}
intsDisable();
/* Remove from current thread list */
for (ntid = &threadQhead[tid->PrivateInfo->priority]; *ntid != 0; ntid = &(*ntid)->next) {
if (*ntid == tid) {
*ntid = tid->next;
break;
}
}
/* Insert onto a new one */
tid->priority = prio;
tid->PrivateInfo->priority = (uint8)tid->priority;
if (threadQhead[prio] == 0) {
threadQhead[prio] = tid;
threadQtail[prio] = tid;
if (prio > currentThread->PrivateInfo->priority) {
needReschedule = true;
}
}
else {
threadQtail[prio]->next = tid;
threadQtail[prio] = tid;
}
tid->next = 0;
intsRestore();
}
/*
* Put a thread to sleep.
*/
void
sleepThread(int64 time)
{
thread** tidp;
/* Sleep for no time */
if (time == 0) {
return;
}
intsDisable();
/* Get absolute time */
currentThread->PrivateInfo->time = time + currentTime();
/* Find place in alarm list */
for (tidp = &alarmList; (*tidp) != 0; tidp = &(*tidp)->next) {
if ((*tidp)->PrivateInfo->time > currentThread->PrivateInfo->time) {
break;
}
}
/* If I'm head of alarm list, restart alarm */
if (tidp == &alarmList) {
alarm((int)((time + 999) / 1000));
}
/* Suspend thread on it */
suspendOnQThread(currentThread, tidp);
intsRestore();
}
/*
* Handle alarm.
*/
static
void
alarmException(int sig)
{
thread* tid;
int64 time;
/* Re-enable signal - necessary for SysV */
signal(sig, (SIG_T)alarmException);
intsDisable();
/* Wake all the threads which need waking */
time = currentTime();
while (alarmList != 0 && alarmList->PrivateInfo->time <= time) {
tid = alarmList;
alarmList = alarmList->next;
resumeThread(tid);
}
/* Restart alarm */
if (alarmList != 0) {
alarm((int)((alarmList->PrivateInfo->time - time + 999) / 1000));
}
intsRestore();
}
/*
* Is this thread alive?
*/
bool
aliveThread(thread* tid)
{
if (tid->PrivateInfo != 0 && tid->PrivateInfo->status != THREAD_DEAD) {
return (true);
}
else {
return (false);
}
}
/*
* How many stack frames have I invoked?
*/
long
framesThread(thread* tid)
{
long count;
THREADFRAMES(tid, count);
return (count);
}
/*
* Reschedule the thread.
* Called whenever a change in the running thread is required.
*/
void
reschedule(void)
{
int i;
thread* lastThread;
int b;
/* Check events - we may release a high priority thread */
checkEvents(false);
for (;;) {
for (i = MAX_THREAD_PRIO; i >= MIN_THREAD_PRIO; i--) {
if (threadQhead[i] != 0) {
if (threadQhead[i] != currentThread) {
b = blockInts;
#if defined(INTERPRETER)
currentThread->PrivateInfo->exceptPtr = (void*)cjbuf;
cjbuf = 0;
#endif
lastThread = currentThread;
currentThread = threadQhead[i];
DBG( printf("ctxswitch %x -> %x\n", lastThread, currentThread); )
THREADSWITCH(currentThread->PrivateInfo, lastThread->PrivateInfo);
#if defined(INTERPRETER)
cjbuf = (jmp_buf*)currentThread->PrivateInfo->exceptPtr;
#endif
blockInts = b;
}
/* Now we kill the schedule and turn ints
back on */
needReschedule = false;
return;
}
}
/* Nothing to run - wait for external event */
checkEvents(true);
}
}
/*
* Start a daemon thread.
*/
static
thread*
startDaemon(void* func, char* nm)
{
thread* tid;
DBG( printf("startDaemon %s\n", nm); )
/* Keep daemon threads as root objects */
tid = (thread*)alloc_object(ThreadClass, true);
assert(tid != 0);
tid->PrivateInfo = calloc(sizeof(ctx), 1);
tid->name = makeJavaCharArray(nm, strlen(nm));
tid->priority = MAX_THREAD_PRIO;
tid->PrivateInfo->priority = (uint8)tid->priority;
tid->next = 0;
tid->PrivateInfo->status = THREAD_SUSPENDED;
assert(tid->PrivateInfo != 0);
/* Insert into live list as long as it's not the finaliser */
if (func != &finaliserMan) {
tid->PrivateInfo->nextlive = liveThreads;
liveThreads = tid;
}
ALLOCTHREADSTACK(tid);
tid->single_step = 0;
tid->daemon = 1;
tid->stillborn = 0;
tid->target = 0;
tid->interruptRequested = 0;
tid->group = 0;
/* Construct the initial restore point. */
THREADINIT(tid->PrivateInfo, func);
talive++;
tdaemon++;
return (tid);
}
#endif