📄 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 "config.h"#if defined(HAVE_SYS_TYPES_H)#include <sys/types.h>#endif /* defined(HAVE_SYS_TYPES_H) */#if defined(HAVE_SYS_WAIT_H)#include <sys/wait.h>#endif /* defined(HAVE_SYS_WAIT_H) */#include "jthread.h"#include "jsignal.h"#include "xprofiler.h"#include "jqueue.h"/* For NOTIMEOUT */#include "jsyscall.h"/* For jlong */#include "jni_md.h"/* For Hjava_lang_VMThread */#include "thread.h"#include "gc.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/* * 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 */#define JTHREADQ(q) ((jthread *)(q)->element)static KaffePool *queuePool; /* pool of single-linked node */static KaffeNodeQueue**threadQhead; /* double-linked run queue */ static KaffeNodeQueue**threadQtail;static KaffeNodeQueue* liveThreads; /* list of all live threads */static KaffeNodeQueue* alarmList; /* list of all threads on alarm queue */static KaffeNodeQueue* 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 KaffeNodeQueue* readQ[FD_SETSIZE]; /* threads blocked on read */static KaffeNodeQueue* writeQ[FD_SETSIZE]; /* threads blocked on write */static jboolean blockingFD[FD_SETSIZE]; /* file descriptor which should really block */static jmutex threadLock; /* static lock to protect liveThreads etc. */static jmutex GClock;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 *//** This is the garbage collector to use to allocate thread data. */static Collector *threadCollector;/* * the following variables are set by jthread_init, and show how the * threading system is parametrized. */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 = NULL;static jthread* firstThread = NULL;/* Context switch related functions */#ifndef JTHREAD_CONTEXT_SAVE#define JTHREAD_CONTEXT_SAVE(buf) JTHREAD_SETJMP((buf))#endif#ifndef JTHREAD_CONTEXT_RESTORE#define JTHREAD_CONTEXT_RESTORE(buf, val) JTHREAD_LONGJMP((buf), (val))#endif/* 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);static void addWaitQThread(jthread *jtid, KaffeNodeQueue **queue);static void cleanupWaitQ(jthread *jtid);/* * 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(KaffeNodeQueue *list, jthread *t){ for (; list != NULL; list = list->next) { if (JTHREADQ(list) == t) { return (1); } } return (0);}/* * yield to another thread */static inline voidinternalYield(void){ int priority = currentJThread->priority; if (threadQhead[priority] != 0 && threadQhead[priority] != threadQtail[priority]) { /* Get the first thread and move it to the end */ KaffeNodeQueue *firstThreadNode = threadQhead[priority]; threadQhead[priority] = firstThreadNode->next; threadQtail[priority]->next = firstThreadNode; threadQtail[priority] = firstThreadNode; firstThreadNode->next = 0; needReschedule = true; }}static voidaddToAlarmQ(jthread* jtid, jlong timeout){ KaffeNodeQueue** tidp; KaffeNodeQueue* node; 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)->next) { if (JTHREADQ(*tidp)->time > jtid->time) { break; } } node = KaffePoolNewNode(queuePool); node->next = *tidp; node->element = jtid; *tidp = node; /* If I'm head of alarm list, restart alarm */ if (tidp == &alarmList) { MALARM(timeout); } } else { /* Huge timeout value, ignore it. */ }}static voidremoveFromAlarmQ(jthread* jtid){ KaffeNodeQueue** tidp; assert(intsDisabled()); jtid->flags &= ~THREAD_FLAGS_ALARM; /* Find thread in alarm list and remove it */ for (tidp = &alarmList; (*tidp) != 0; tidp = &(*tidp)->next) { if (JTHREADQ(*tidp) == jtid) { KaffeNodeQueue *node = *tidp; (*tidp) = node->next; KaffePoolReleaseNode(queuePool, node); 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){ /* KAFFE_VMDEBUG */ 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( currentJThread->status != THREAD_SUSPENDED ) {#ifdef ENABLE_JVMPI EXCEPTIONFRAME(jthread_current()->localData.topFrame, sc);#endif } /* * 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 defined(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 defined(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, (void*)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 UNUSED, SIGNAL_CONTEXT_POINTER(sc UNUSED)){ 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -