📄 jthread.c
字号:
/* * jthread.c * Java thread package - derived from thread-internal.c * * Internal threading system support * * Copyright (c) 1996, 1997, 1998 * Transvirtual Technologies, Inc. All rights reserved. * * See the file "license.terms" for information on usage and redistribution * of this file. * * Written by Godmar Back <gback@cs.utah.edu> and * Tim Wilkinson <tim@transvirtual.com> */#include "jthread.h"#include "jsignal.h"#include "xprofiler.h"/* Flags used for threading I/O calls */#define TH_READ 0#define TH_WRITE 1#define TH_ACCEPT TH_READ#define TH_CONNECT TH_WRITE/* thread status */#define THREAD_SUSPENDED 0#define THREAD_RUNNING 1#define THREAD_DEAD 2/* thread flags */#define THREAD_FLAGS_GENERAL 0#define THREAD_FLAGS_NOSTACKALLOC 1 /* this flag is not used anymore */#define THREAD_FLAGS_KILLED 2#define THREAD_FLAGS_ALARM 4#define THREAD_FLAGS_EXITING 8#define THREAD_FLAGS_DONTSTOP 16#define THREAD_FLAGS_DYING 32#define THREAD_FLAGS_BLOCKEDEXTERNAL 64#define THREAD_FLAGS_INTERRUPTED 128/* * If option DETECTDEADLOCK is given, detect deadlocks. * A deadlock is defined as a situation where no thread is runnable and * no threads is blocked on a timer, IO, or other external events. * * Undeffing this will save a few cycles, but kaffe will just hang if * there is a deadlock. */#define DETECTDEADLOCK#if defined(DETECTDEADLOCK)#define BLOCKED_ON_EXTERNAL(t) \ do { \ tblocked_on_external++; \ t->flags |= THREAD_FLAGS_BLOCKEDEXTERNAL; \ } while (0)#define CLEAR_BLOCKED_ON_EXTERNAL(t) \ do { \ if (t->flags & THREAD_FLAGS_BLOCKEDEXTERNAL) { \ tblocked_on_external--; \ t->flags &= ~THREAD_FLAGS_BLOCKEDEXTERNAL; \ } \ } while (0)/* number of threads blocked on external events */static int tblocked_on_external;#else /* !DETECTDEADLOCK */#define BLOCKED_ON_EXTERNAL(t)#define CLEAR_BLOCKED_ON_EXTERNAL(t)#endif/* * Variables. * These should be kept static to ensure encapsulation. */static int preemptive = true; /* enable preemptive scheduling */static int talive; /* number of threads alive */static int tdaemon; /* number of daemons alive */static void (*runOnExit)(void); /* function to run when all non-daemon die */static jthread**threadQhead; /* double-linked run queue */ static jthread**threadQtail;static jthread* liveThreads; /* list of all live threads */static jthread* alarmList; /* list of all threads on alarm queue */static jthread* waitForList; /* list of all threads waiting for a child */static int maxFd = -1; /* highest known fd */static fd_set readsPending; /* fds we want to read from */static fd_set writesPending; /* fds we want to write to */static jthread* readQ[FD_SETSIZE]; /* threads blocked on read */static jthread* writeQ[FD_SETSIZE]; /* threads blocked on write */static jmutex threadLock; /* static lock to protect liveThreads etc. */static int sigPending; /* flags that says whether a intr is pending */static int pendingSig[NSIG]; /* array that says which intrs are pending */static int sigPipe[2]; /* a pipe to ensure we don't lose our wakeup */static int bytesInPipe; /* total number of bytes written to pipe */static int wouldlosewakeup; /* a flag that says whether we're past the point where we check for pending signals before sleeping in select() */static int blockInts; /* counter that says whether irqs are blocked */static int needReschedule; /* is a change in the current thread required *//* * the following variables are set by jthread_init, and show how the * threading system is parametrized. */static void *(*allocator)(size_t); /* malloc */static void (*deallocator)(void*); /* free */static void (*destructor1)(void*); /* call when a thread exits */static void (*onstop)(void); /* call when a thread is stopped */static void (*ondeadlock)(void); /* call when we detect deadlock */static int max_priority; /* maximum supported priority */static int min_priority; /* minimum supported priority */jthread* currentJThread;/* The arguments to a signal handler */#ifndef SIGNAL_ARGS#define SIGNAL_ARGS(sig, sc) int sig#endif/* Get a signal context pointer from signal arguments */#ifndef GET_SIGNAL_CONTEXT_POINTER#define GET_SIGNAL_CONTEXT_POINTER(x) 0#endif/* A signal context pointer type, used in parameter lists/declarations */#ifndef SIGNAL_CONTEXT_POINTER#define SIGNAL_CONTEXT_POINTER(x) void *##x#endif/* Get the PC from a signal context pointer */#ifndef SIGNAL_PC#define SIGNAL_PC(scp) 0#endif/* * Function declarations. * Again, keep these static to ensure encapsulation. */static void handleInterrupt(int sig, SIGNAL_CONTEXT_POINTER(sc));static void interrupt(SIGNAL_ARGS(sig, sc));static void childDeath(void);static void handleIO(int);static void killThread(jthread *jtid);static void resumeThread(jthread* jtid);static void reschedule(void);static void restore_fds(void);static void restore_fds_and_exit(void);static void die(void);static int jthreadedFileDescriptor(int fd);static void intsDisable(void);static void intsRestore(void);/* * macros to set and extract stack pointer from jmp_buf * make sure SP_OFFSET has the correct value for your architecture! */#define GET_SP(E) (((void**)(E))[SP_OFFSET])#define SET_SP(E, V) ((void**)(E))[SP_OFFSET] = (V)#define GET_FP(E) (((void**)(E))[FP_OFFSET])#define SET_FP(E, V) ((void**)(E))[FP_OFFSET] = (V)/* * Macros to set and extract backing store pointer from jmp_buf * (IA-64 specific) */#if defined(__ia64__)#define BSP_OFFSET 17#define GET_BSP(E) (((void**)(E))[BSP_OFFSET])#define SET_BSP(E, V) ((void**)(E))[BSP_OFFSET] = (V)#endif/* Set the base pointer in a jmp_buf if we can (only a convenience) */#if defined(BP_OFFSET)#define SET_BP(E, V) ((void**)(E))[BP_OFFSET] = (V)#endif/* amount of stack space to be duplicated at stack creation time */#if !defined(STACK_COPY)#define STACK_COPY (32*4)#endif#if defined(HAVE_SYS_WAIT_H)#include <sys/wait.h>#endif/* Select an alarm system */#if defined(HAVE_SETITIMER) && defined(ITIMER_REAL)#define MALARM(_mt) \ { \ struct itimerval tm; \ tm.it_interval.tv_sec = 0; \ tm.it_interval.tv_usec = 0; \ tm.it_value.tv_sec = (_mt) / 1000; \ tm.it_value.tv_usec = ((_mt) % 1000) * 1000; \ setitimer(ITIMER_REAL, &tm, 0); \ }#elif defined(HAVE_ALARM)#define MALARM(_mt) alarm((int)(((_mt) + 999) / 1000))#endif/*============================================================================ * * Functions related to list manipulation and interrupt handling * *//* * Check whether a thread is on a given list */static intisOnList(jthread *list, jthread *t){ for (; list; list = list->nextQ) { if (list == t) { return (1); } } return (0);}/* * yield to another thread */static inline voidinternalYield(void){ int priority = currentJThread->priority; if (threadQhead[priority] && threadQhead[priority] != threadQtail[priority]) { /* Get the first thread and move it to the end */ jthread *firstThread = threadQhead[priority]; threadQhead[priority] = firstThread->nextQ; threadQtail[priority]->nextQ = firstThread; threadQtail[priority] = firstThread; firstThread->nextQ = 0; needReschedule = true; }}static voidaddToAlarmQ(jthread* jtid, jlong timeout){ jthread** tidp; jlong ct; assert(intsDisabled()); ct = currentTime(); if( (timeout + ct) > ct ) { jtid->flags |= THREAD_FLAGS_ALARM; /* Get absolute time */ jtid->time = timeout + ct; /* Find place in alarm list and insert it */ for (tidp = &alarmList; (*tidp) != 0; tidp = &(*tidp)->nextalarm) { if ((*tidp)->time > jtid->time) { break; } } jtid->nextalarm = *tidp; *tidp = jtid; /* If I'm head of alarm list, restart alarm */ if (tidp == &alarmList) { MALARM(timeout); } } else { /* Huge timeout value, ignore it. */ }}static voidremoveFromAlarmQ(jthread* jtid){ jthread** tidp; assert(intsDisabled()); jtid->flags &= ~THREAD_FLAGS_ALARM; /* Find thread in alarm list and remove it */ for (tidp = &alarmList; (*tidp) != 0; tidp = &(*tidp)->nextalarm) { if ((*tidp) == jtid) { (*tidp) = jtid->nextalarm; jtid->nextalarm = 0; break; } }}/* * check whether interrupts are disabled */intintsDisabled(void){ return (blockInts > 0);}/* * disable interrupts * * Instead of blocking signals, we increment a counter. * If a signal comes in while the counter is non-zero, we set a pending flag * and mark the signal as pending. * * intsDisable may be invoked recursively. (is that really a good idea? - gb) */static inline void intsDisable(void){ blockInts++;}static inline voidprocessSignals(void){ int i; for (i = 1; i < NSIG; i++) { if (pendingSig[i]) { pendingSig[i] = 0; handleInterrupt(i, 0); } } sigPending = 0;}/* * restore interrupts * * If interrupts are about to be reenabled, execute the handlers for all * signals that are pending. */static inline voidintsRestore(void){ /* DEBUG */ assert(blockInts >= 1); if (blockInts == 1) { if (sigPending) { processSignals(); } /* reschedule if necessary */ if (needReschedule == true) { reschedule(); } } blockInts--;}/* * Prevent all other threads from running. * In this uniprocessor implementation, this is simple. */void jthread_suspendall(void){ intsDisable();}/* * Reallow other threads. * In this uniprocessor implementation, this is simple. */void jthread_unsuspendall(void){ intsRestore();} /* * Handle an asynchronous signal (i.e. a software interrupt). * * This is the handler given to registerAsyncSignalHandler(). * * It is guaranteed that all asynchronous signals are delayed when * this handler begins execution (see registerAsyncSignalHandler()). * There are two ways for the asynchronous signals to get unblocked: * (1) return from the function. The OS will unblock them. (2) * explicitly unblock the signals. We must do this before performing * a thread context switch as the target thread should (obviously) not * be running with all signals blocked. */static voidinterrupt(SIGNAL_ARGS(sig, sc)){ /* * If ints are blocked, this might indicate an inconsistent state of * one of the thread queues (either alarmList or threadQhead/tail). * * Record this interrupt as pending so that the forthcoming * intsRestore() (the intsRestore() in the interrupted thread) * will handle it. Then return from the signal handler. * * Also mark the interrupt as pending if interrupts are not disabled, * but the wouldlosewakeup flag is set. This is the case before * we go in select/poll. */ if (intsDisabled() || wouldlosewakeup) { char c; pendingSig[sig] = 1; sigPending = 1; #if defined(KAFFE_XPROFILER) /* * Since the regular handler won't run with the sig context we * need to do the hit here */ if( sig == SIGVTALRM ) { SIGNAL_CONTEXT_POINTER(scp) = GET_SIGNAL_CONTEXT_POINTER(sc); profileHit((char *)SIGNAL_PC(scp)); }#endif /* * There is a race condition in handleIO() between * zeroing blockints and going into select(). * sigPipe+wouldlosewakeup is the hack that avoids * that race condition. See handleIO(). * * If we would lose the wakeup because we're about to go to * sleep in select(), write into the sigPipe to ensure select * returns. */ /* * Write a byte in the pipe if we get a signal if * wouldlosewakeup is set. * Do not write more than one byte, however. */ if (wouldlosewakeup == 1) { write(sigPipe[1], &c, 1); bytesInPipe++; wouldlosewakeup++; }#if KAFFE_SIGNAL_ONE_SHOT /* * On some systems, signal handlers are a one-shot deal. * Re-install the signal handler for those systems. */ restoreAsyncSignalHandler(sig, interrupt);#endif /* * Returning from the signal handler should restore * all signal state (if the OS is not broken). */ return; } /* * The interrupted code was not in a critical section, * so we enter a critical section now. Note that we * will *not* be interrupted between the blockInts * check above and the intsDisable() below because * the signal mask delays all asynchronous signals. */ intsDisable();#if KAFFE_SIGNAL_ONE_SHOT /* Re-enable signal if necessary */ restoreAsyncSignalHandler(sig, interrupt);#endif /* * Restore the signal state. This means unblock all * asynchronous signals. We can now context switch to another * thread as the signal state for the Kaffe process is clear * in the eyes of the OS. Any asynchronous signals that come * in because we just unblocked them will discover that * blockInts > 0, and flag their arrival in the pendingSig[] * array. * * We clear the signal's pending indicator before reallowing signals. */ pendingSig[sig] = 0; unblockAsyncSignals(); /* * Handle the signal. */ handleInterrupt(sig, GET_SIGNAL_CONTEXT_POINTER(sc)); /* * Leave the critical section. This may or may not cause a * reschedule. (Depends on the side-effects of * handleInterrupt()). */ intsRestore();}/* * handle a SIGVTALRM alarm. * * If preemption is disabled, we have the current thread so that it is * scheduled in a round-robin fashion with its peers who have the same * priority. */static void handleVtAlarm(int sig, SIGNAL_CONTEXT_POINTER(sc)){ static int c;#if defined(KAFFE_XPROFILER) if( sc ) profileHit((char *)SIGNAL_PC(sc));#endif if (preemptive) { internalYield(); } /* * This is kind of ugly: some fds won't send us SIGIO. * Example: the pseudo-tty driver in FreeBSD won't send a signal * if we blocked on a write because the output buffer was full, and * the output buffer became empty again. * * So we check periodically, every 0.2 seconds virtual time. */ if (++c % 20 == 0) { handleIO(false); }}/* * handle a SIGALRM alarm. */static void alarmException(void){ jthread* jtid; jlong time; /* Wake all the threads which need waking */ time = currentTime(); while (alarmList != 0 && alarmList->time <= time) { /* Restart thread - this will tidy up the alarm and blocked * queues. */ jtid = alarmList; alarmList = alarmList->nextalarm; jtid->flags |= THREAD_FLAGS_INTERRUPTED; resumeThread(jtid); } /* Restart alarm */ if (alarmList != 0) { MALARM(alarmList->time - time); }}/* * print thread flags in pretty form. */static char*printflags(unsigned i){ static char b[256]; /* plenty */ struct { int flagvalue; char *flagname; } flags[] = { { THREAD_FLAGS_GENERAL, "GENERAL" }, { THREAD_FLAGS_NOSTACKALLOC, "NOSTACKALLOC" }, { THREAD_FLAGS_KILLED, "KILLED" }, { THREAD_FLAGS_ALARM, "ALARM" }, { THREAD_FLAGS_EXITING, "EXITING" }, { THREAD_FLAGS_DONTSTOP, "DONTSTOP" }, { THREAD_FLAGS_DYING, "DYING" }, { THREAD_FLAGS_BLOCKEDEXTERNAL, "BLOCKEDEXTERNAL" }, { THREAD_FLAGS_INTERRUPTED, "INTERRUPTED" }, { 0, NULL } }, *f = flags; b[0] = '\0'; while (f->flagname) { if (i & f->flagvalue) { strcat(b, f->flagname); strcat(b, " "); } f++; } return b;}/* * dump information about a thread to stderr */voidjthread_dumpthreadinfo(jthread_t tid){ dprintf("tid %p, status %s flags %s\n", tid, tid->status == THREAD_SUSPENDED ? "SUSPENDED" : tid->status == THREAD_RUNNING ? "RUNNING" : tid->status == THREAD_DEAD ? "DEAD" : "UNKNOWN!!!", printflags(tid->flags)); if (tid->blockqueue != NULL) { jthread *t; int i; dprintf(" blocked"); if (isOnList(waitForList, tid)) { dprintf(": waiting for children"); }#if 0 /* XXX FIXME: alarmList uses nextalarm, but isOnList iterates * using nextQ */ if (isOnList(alarmList, tid)) { dprintf(": sleeping"); }#endif for (i = 0; i < FD_SETSIZE; i++) { if (isOnList(readQ[i], tid)) { dprintf(": reading from fd %d ", i); break; } if (isOnList(writeQ[i], tid)) { dprintf(": writing to fd %d ", i); break; } } dprintf("@%p (%p->", tid->blockqueue, t = *tid->blockqueue); while (t && t->nextQ) { t = t->nextQ; dprintf("%p->", t); } dprintf("|) "); }}/* * handle an interrupt. * * this function is either invoked from within a signal handler, or as the * result of intsRestore. */static void handleInterrupt(int sig, SIGNAL_CONTEXT_POINTER(sc)){ switch(sig) { case SIGALRM: alarmException(); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -