📄 threads.c
字号:
#include "types.h"#include "trace.h"#include "platform_hooks.h"#include "constants.h"#include "specialsignatures.h"#include "specialclasses.h"#include "threads.h"#include "classes.h"#include "language.h"#include "configure.h"#include "interpreter.h"#include "memory.h"#include "exceptions.h"#include "stack.h"#define NO_OWNER 0x00#define get_stack_frame() ((StackFrame *) (currentThread->currentStackFrame))/** * Thread currently being executed by engine(). Threads exist in an * intrinsic circular list. */Thread* currentThread;/** * Priority queue of threads. Entry points at the last thread in the queue. */Thread *threadQ[10];/** * Thread id generator, always increasing. */byte gThreadCounter;/** * Current program number, i.e. number of 'main()'s hanging around */byte gProgramNumber;StackFrame *current_stackframe(){ byte arraySize; arraySize = currentThread->stackFrameArraySize; if (arraySize == 0) return null; return stackframe_array() + (arraySize - 1);}void update_stack_frame (StackFrame *stackFrame){ stackFrame->stackTop = stackTop; stackFrame->pc = pc;} void update_registers (StackFrame *stackFrame){ pc = stackFrame->pc; stackTop = stackFrame->stackTop; localsBase = stackFrame->localsBase;}/* Turns out inlines aren't really inlined.inline byte get_thread_id (Object *obj){ return obj->threadId;}inline void set_thread_id (Object *obj, byte threadId){ obj->threadId = threadId;}inline void inc_monitor_count (Object *obj){ obj->monitorCount++;}inline void set_monitor_count (Object *obj, byte count){ obj->monitorCount = count;}*/#define get_thread_id(obj) ((obj)->threadId)#define set_thread_id(obj,_threadId) ((obj)->threadId = (_threadId))#define inc_monitor_count(obj) ((obj)->monitorCount++)#define set_monitor_count(obj,count) ((obj)->monitorCount = (count))/** * Allocate stack frames * Allocate ID * Insert into run list * Mark thread as STARTED */boolean init_thread (Thread *thread){ thread->threadId = gThreadCounter + 1; // Catch the primordial thread if (currentThread == null) thread->priority = NORM_PRIORITY; #if DEBUG_THREADS printf("Setting intial priority to %d\n", thread->priority); #endif if (thread->state != NEW) { throw_exception(illegalStateException); return false; } // Allocate space for stack frames. thread->stackFrameArray = ptr2word (new_primitive_array (T_STACKFRAME, INITIAL_STACK_FRAMES)); if (thread->stackFrameArray == JNULL) return false; // Allocate actual stack storage (INITIAL_STACK_SIZE * 4 bytes) thread->stackArray = ptr2word (new_primitive_array (T_INT, INITIAL_STACK_SIZE)); if (thread->stackArray == JNULL) { free_array (ref2obj(thread->stackFrameArray)); thread->stackFrameArray = JNULL; return false; } gThreadCounter++; #ifdef VERIFY assert (is_array (word2obj (thread->stackFrameArray)), THREADS0); assert (is_array (word2obj (thread->stackArray)), THREADS1); #endif thread->stackFrameArraySize = 0; thread->state = STARTED; if (currentThread == null) currentThread = thread; enqueue_thread(thread); return true;}/** * Switches to next thread: * * do * get next thread * if waiting, grab monitor and run * if sleeping and timer expired, run * if DEAD, clean up and use current thread * if started, initialize and run * until selected thread can run * * @return false iff there are no live threads * to switch to. */ boolean switch_thread(){ Thread *anchorThread, *previousThread, *candidate; Thread **pThreadQ; boolean nonDaemonRunnable = false; StackFrame *stackFrame = null; short i; #if DEBUG_THREADS || DEBUG_BYTECODE printf ("------ switch_thread: currentThread at %d\n", (int) currentThread); #endif if (currentThread != null) { // Only current threads can die. Tidy up dead threads if (currentThread->state == DEAD) { #if DEBUG_THREADS printf ("Tidying up DEAD thread %d: %d\n", (int) currentThread, (int)currentThread->threadId); #endif #if REMOVE_DEAD_THREADS // This order of deallocation is actually crucial to avoid leaks free_array ((Object *) word2ptr (currentThread->stackArray)); free_array ((Object *) word2ptr (currentThread->stackFrameArray)); #ifdef SAFE currentThread->stackFrameArray = JNULL; currentThread->stackArray = JNULL; #endif // SAFE #endif // REMOVE_DEAD_THREADS // Remove thread from queue. dequeue_thread(currentThread); } else { // Save context information stackFrame = current_stackframe();#if DEBUG_THREADS printf ("switchThread: current stack frame: %d\n", (int) stackFrame);#endif if (stackFrame != null) { update_stack_frame (stackFrame); } } } currentThread = null; // Loop until a frame is found that can be made to run. for (i=MAX_PRIORITY-1; i >= 0; i--) { pThreadQ = &threadQ[i]; previousThread = anchorThread = *pThreadQ; if (!previousThread) continue; do { candidate = word2ptr(previousThread->nextThread); #if DEBUG_THREADS printf ("Checking state of thread %d(%d)(s=%d,p=%d,i=%d,d=%d)\n", (int)candidate, (int)candidate->threadId, (int)candidate->state, (int)candidate->priority, (int)candidate->interruptState, (int)candidate->daemon ); #endif // See if we can move a thread to the running state. Used to not do this if we // had already found one, but turns out to be easiest if we are avoiding // priority inversion. switch (candidate->state) { case CONDVAR_WAITING: // We are waiting to be notified if ((candidate->sleepUntil > 0) && (get_sys_time() >= (FOURBYTES) candidate->sleepUntil)) {#if DEBUG_MONITOR printf ("Waking up waiting thread %d: %d\n", (int) candidate, candidate->threadId);#endif // We will drop through to mon waiting. } else if (candidate->interruptState == INTERRUPT_CLEARED) break; else candidate->interruptState = INTERRUPT_GRANTED; // candidate->state = MON_WAITING; // drop through case MON_WAITING: { Object *pObj = word2obj(candidate->waitingOn); byte threadId = get_thread_id(pObj); // We are waiting to enter a synchronized block #ifdef VERIFY assert (pObj != JNULL, THREADS6); #endif if (threadId == NO_OWNER) { // NOW enter the monitor (guaranteed to succeed) enter_monitor(candidate, pObj); // Set the monitor depth to whatever was saved. set_monitor_count(pObj, candidate->monitorCount); // Let the thread run. candidate->state = RUNNING; #ifdef SAFE candidate->waitingOn = JNULL; #endif }#if PI_AVOIDANCE // Only avoid priority inversion if we don't already have a thread to run. else if (currentThread == null) { Thread *pOwner; int j; // Track down who owns this monitor and run them instead. // Could be 'waiting' in a native method, or we could be deadlocked!find_next: if (candidate->threadId != threadId) { for (j=MAX_PRIORITY-1; j >= 0; j--) { pOwner = threadQ[j]; if (!pOwner) continue; do { // Remember threadQ[j] is the last thread on the queue pOwner = word2ptr(pOwner->nextThread); if (pOwner->threadId == threadId) { if (pOwner->state == RUNNING) { currentThread = pOwner; goto done_pi; } // if owner is waiting too, iterate down. if (pOwner->state == MON_WAITING) { threadId = get_thread_id(word2obj(pOwner->waitingOn)); if (threadId != NO_OWNER) goto find_next; } } } while (pOwner != threadQ[j]); } // If we got here, we're in trouble, just drop through. } }done_pi: break; ;#endif // PI_AVOIDANCE } break; case SLEEPING: if (candidate->interruptState != INTERRUPT_CLEARED || (get_sys_time() >= (FOURBYTES) candidate->sleepUntil)) { #if DEBUG_THREADS printf ("Waking up sleeping thread %d: %d\n", (int) candidate, candidate->threadId); #endif candidate->state = RUNNING; if (candidate->interruptState != INTERRUPT_CLEARED) candidate->interruptState = INTERRUPT_GRANTED; #ifdef SAFE candidate->sleepUntil = JNULL; #endif // SAFE } break; case STARTED: if (currentThread == null) { // Put stack ptr at the beginning of the stack so we can push arguments // to entry methods. This assumes set_top_word or set_top_ref will // be called immediately below. #if DEBUG_THREADS printf ("Starting thread %d: %d\n", (int) candidate, candidate->threadId); #endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -