📄 jthread.c
字号:
case SIGUSR1: ondeadlock(); break;#if defined(SIGVTALRM) case SIGVTALRM: handleVtAlarm(sig, sc); break;#endif case SIGCHLD: childDeath(); break; case SIGIO: handleIO(false); break; default: dprintf("unknown signal %d\n", sig); exit(-1); }}/*============================================================================ * * Functions related to run queue manipulation *//* * Resume a thread running. * This routine has to be called only from locations which ensure * run / block queue consistency. There is no check for illegal resume * conditions (like explicitly resuming an IO blocked thread). */static voidresumeThread(jthread* jtid){ jthread** ntid;DBG(JTHREAD, dprintf("resumeThread %p\n", jtid); ) intsDisable(); if (jtid->status != THREAD_RUNNING) { CLEAR_BLOCKED_ON_EXTERNAL(jtid); /* Remove from alarmQ if necessary */ if ((jtid->flags & THREAD_FLAGS_ALARM) != 0) { removeFromAlarmQ(jtid); } /* Remove from lockQ if necessary */ if (jtid->blockqueue != 0) { for (ntid = jtid->blockqueue; *ntid != 0; ntid = &(*ntid)->nextQ) { if (*ntid == jtid) { *ntid = jtid->nextQ; break; } } jtid->blockqueue = 0; } jtid->status = THREAD_RUNNING; /* Place thread on the end of its queue */ if (threadQhead[jtid->priority] == 0) { threadQhead[jtid->priority] = jtid; threadQtail[jtid->priority] = jtid; if (jtid->priority > currentJThread->priority) { needReschedule = true; } } else { threadQtail[jtid->priority]->nextQ = jtid; threadQtail[jtid->priority] = jtid; } jtid->nextQ = 0; } else {DBG(JTHREAD, dprintf("Re-resuming %p\n", jtid); ) } intsRestore();}/* * Suspend a thread on a queue. * Return true if thread was interrupted. */static intsuspendOnQThread(jthread* jtid, jthread** queue, jlong timeout){ int rc = false; jthread** ntid; jthread* last;DBG(JTHREAD, dprintf("suspendOnQThread %p %p (%qd) bI %d\n", jtid, queue, timeout, blockInts); ) assert(intsDisabled()); if (jtid->status != THREAD_SUSPENDED) { jtid->status = THREAD_SUSPENDED; last = 0; for (ntid = &threadQhead[jtid->priority]; *ntid != 0; ntid = &(*ntid)->nextQ) { if (*ntid == jtid) { /* Remove thread from runq */ *ntid = jtid->nextQ; if (*ntid == 0) { threadQtail[jtid->priority] = last; } /* Insert onto head of lock wait Q */ if (queue != 0) { jtid->nextQ = *queue; *queue = jtid; jtid->blockqueue = queue; } /* If I have a timeout, insert into alarmq */ if (timeout > NOTIMEOUT) { addToAlarmQ(jtid, timeout); } /* If I was running, reschedule */ if (jtid == currentJThread) { reschedule(); if (jtid->flags & THREAD_FLAGS_INTERRUPTED) { jtid->flags &= ~THREAD_FLAGS_INTERRUPTED; rc = true; } } break; } last = *ntid; } } else { DBG(JTHREAD, dprintf("Re-suspending %p on %p\n", jtid, *queue); ) } return (rc);}/* * Kill thread. */static void killThread(jthread *tid){ jthread **ntid; jthread* last; intsDisable(); /* allow thread to perform any action before being killed - * such as notifying on the object */ if (destructor1) (*destructor1)(tid->jlThread);DBG(JTHREAD, dprintf("killThread %p kills %p\n", currentJThread, tid); ) if (tid->status != THREAD_DEAD) { /* Get thread off runq (if it needs it) */ if (tid->status == THREAD_RUNNING) { int pri = tid->priority; last = 0; for (ntid = &threadQhead[pri]; *ntid != 0; ntid = &(*ntid)->nextQ) { if (*ntid == tid) { *ntid = tid->nextQ; if (*ntid == 0) threadQtail[pri] = last; break; } last = *ntid; } } /* Run something else */ if (currentJThread == tid) { needReschedule = true; blockInts = 1; } /* Now that we are off the runQ, it is safe to leave * the list of live threads and be GCed. */ /* Remove thread from live list so it can be garbaged */ for (ntid = &liveThreads; *ntid != 0; ntid = &(*ntid)->nextlive) { if (tid == (*ntid)) { (*ntid) = tid->nextlive; break; } } /* Dead Jim - let the GC pick up the remains */ tid->status = THREAD_DEAD; } intsRestore();}/*============================================================================ * * Functions dealing with thread contexts and the garbage collection interface * *//* * Allocate a new thread context and stack. */static jthread*newThreadCtx(int stackSize){ jthread *ct; ct = allocator(sizeof(jthread) + stackSize); if (ct == 0) { return 0; } ct->stackBase = (ct + 1); ct->stackEnd = ct->stackBase + stackSize; ct->restorePoint = ct->stackEnd; ct->status = THREAD_SUSPENDED;DBG(JTHREAD, dprintf("allocating new thread, stack base %p-%p\n", ct->stackBase, ct->stackEnd); ) return (ct);}/* * free a thread context and its stack */void jthread_destroy(jthread *jtid){ jthread *x; if (DBGEXPR(JTHREAD, true, false)) { for (x = liveThreads; x; x = x->nextlive) assert(x != jtid); } deallocator(jtid);}/* * iterate over all live threads */voidjthread_walkLiveThreads(void (*func)(void *jlThread)){ jthread* tid; for (tid = liveThreads; tid != NULL; tid = tid->nextlive) { func(tid->jlThread); }}/* * determine the interesting stack range for a conservative gc */intjthread_extract_stack(jthread *jtid, void **from, unsigned *len){ assert(jtid != NULL);#if defined(STACK_GROWS_UP) *from = jtid->stackBase; *len = jtid->restorePoint - jtid->stackBase;#else *from = jtid->restorePoint; *len = jtid->stackEnd - jtid->restorePoint;#endif return (1);}/* * XXX this is supposed to count the number of stack frames */intjthread_frames(jthread *thrd){ return (0);}/*============================================================================ * * Functions for initialization and thread creation * */#if defined(HAVE_SETITIMER) && defined(ITIMER_VIRTUAL)/* * set virtual timer for 10ms round-robin time-slicing */static voidactivate_time_slicing(void){ struct itimerval tm; tm.it_interval.tv_sec = tm.it_value.tv_sec = 0; tm.it_interval.tv_usec = tm.it_value.tv_usec = 10000;/* 10 ms */ setitimer(ITIMER_VIRTUAL, &tm, 0);}/* * deactivate virtual timer */static voiddeactivate_time_slicing(void){ struct itimerval tm; tm.it_interval.tv_sec = tm.it_value.tv_sec = 0; tm.it_interval.tv_usec = tm.it_value.tv_usec = 0; setitimer(ITIMER_VIRTUAL, &tm, 0);}#elsestatic void activate_time_slicing(void) { }static void deactivate_time_slicing(void) { }#endif/* * Initialize the threading system. */voidjthread_init(int pre, int maxpr, int minpr, void *(*_allocator)(size_t), void (*_deallocator)(void*), void (*_destructor1)(void*), void (*_onstop)(void), void (*_ondeadlock)(void)){ jthread *jtid; int i; /* XXX this is f***ed. On BSD, we get a SIGHUP if we try to put * a process that has a pseudo-tty in async mode in the background * So we'll just ignore it and keep running. Note that this will * detach us from the session too. */ ignoreSignal(SIGHUP);#if defined(SIGVTALRM) registerAsyncSignalHandler(SIGVTALRM, interrupt);#endif registerAsyncSignalHandler(SIGALRM, interrupt); registerAsyncSignalHandler(SIGIO, interrupt); registerAsyncSignalHandler(SIGCHLD, interrupt); registerAsyncSignalHandler(SIGUSR1, interrupt); /* * If debugging is not enabled, set stdin, stdout, and stderr in * async mode. * * If debugging is enabled and ASYNCSTDIO is not given, do not * put them in async mode. * * If debugging is enabled and ASYNCSTDIO is given, put them in * async mode (as if debug weren't enabled.) * * This is useful because large amounts of fprintfs might be * not be seen if the underlying terminal is put in asynchronous * mode. So by default, when debugging, we want stdio be synchronous. * To override this, give the ASYNCSTDIO flag. */#ifndef _HURD_ASYNC_QUICKFIX_ if (DBGEXPR(ANY, DBGEXPR(ASYNCSTDIO, true, false), true)) { for (i = 0; i < 3; i++) { if (i != jthreadedFileDescriptor(i)) { return; } } }#endif /* * On some systems, it is essential that we put the fds back * in their non-blocking state */ atexit(restore_fds); registerTerminalSignal(SIGINT, restore_fds_and_exit); registerTerminalSignal(SIGTERM, restore_fds_and_exit); preemptive = pre; max_priority = maxpr; min_priority = minpr; allocator = _allocator; deallocator = _deallocator; onstop = _onstop; ondeadlock = _ondeadlock; destructor1 = _destructor1; threadQhead = allocator((maxpr + 1) * sizeof (jthread *)); threadQtail = allocator((maxpr + 1) * sizeof (jthread *)); /* create the helper pipe for lost wakeup problem */ if (pipe(sigPipe) != 0) { return; } if (maxFd == -1) { maxFd = sigPipe[0] > sigPipe[1] ? sigPipe[0] : sigPipe[1]; } jtid = newThreadCtx(0); if (!jtid) { return; } /* Fake up some stack bounds until we get the real ones later - we're * the only thread running so this isn't a problem. */ jtid->stackBase = 0; jtid->stackEnd = (char*)0 - 1;#if defined(STACK_GROWS_UP) jtid->restorePoint = jtid->stackEnd;#else jtid->restorePoint = jtid->stackBase;#endif jtid->priority = maxpr; jtid->status = THREAD_SUSPENDED; jtid->flags = THREAD_FLAGS_NOSTACKALLOC; jtid->func = (void (*)(void*))jthread_init; jtid->nextlive = liveThreads; jtid->time = 0; liveThreads = jtid; talive++; currentJThread = jtid; resumeThread(jtid);#if defined(KAFFE_XPROFILER) /* * The profiler is started at program startup, we take over from here * on out so we disable whatever one was installed */ disableProfileTimer();#endif /* Because of the handleVtAlarm hack (poll every 20 SIGVTALRMs) * we turn on the delivery of SIGVTALRM even if no actual time * slicing is possible because only one Java thread is running. * XXX We should be smarter about that. */ activate_time_slicing();}jthread_tjthread_createfirst(size_t mainThreadStackSize, unsigned char prio, void* jlThread){ jthread *jtid; jtid = currentJThread; /* * Note: the stackBase and stackEnd values are used for two purposes: * - to report to the gc what area to scan (extract_stack) * - to help in determining whether the next frame in the link chain * of frames is valid. This is done by checking its range. */#if defined(STACK_GROWS_UP) jtid->stackBase = (void*)((uintp)&jtid & -0x1000); jtid->stackEnd = jtid->stackBase + mainThreadStackSize; jtid->restorePoint = jtid->stackEnd;#else jtid->stackEnd = (void*)(((uintp)&jtid + 0x1000 - 1) & -0x1000); jtid->stackBase = jtid->stackEnd - mainThreadStackSize; jtid->restorePoint = jtid->stackBase;#endif jtid->jlThread = jlThread; jthread_setpriority(jtid, prio); return (jtid);}/* * set a function to be run when all non-daemon threads have exited */void jthread_atexit(void (*f)(void)){ runOnExit = f;}/* * disallow cancellation */void jthread_disable_stop(void){ if (currentJThread) { intsDisable(); currentJThread->flags |= THREAD_FLAGS_DONTSTOP; currentJThread->stopCounter++; assert(currentJThread->stopCounter > 0); /* XXX Shouldn't recurse that much... ever... hopefully. */ assert(currentJThread->stopCounter < 10); intsRestore(); }}/* * reallow cancellation and stop if cancellation pending */void jthread_enable_stop(void){ if (currentJThread) { intsDisable(); if (--currentJThread->stopCounter == 0) { currentJThread->flags &= ~THREAD_FLAGS_DONTSTOP; if ((currentJThread->flags & THREAD_FLAGS_KILLED) != 0 && ((currentJThread->flags & THREAD_FLAGS_EXITING) == 0)) { die(); } } assert(currentJThread->stopCounter >= 0); intsRestore(); }}/* * interrupt a thread */voidjthread_interrupt(jthread *jtid){ intsDisable(); /* make sure we only resume suspended threads * (and neither dead nor runnable threads) */ if (jtid != currentJThread && jtid->status == THREAD_SUSPENDED) { jtid->flags |= THREAD_FLAGS_INTERRUPTED; resumeThread(jtid); } intsRestore();}static void die(void){ currentJThread->flags &= ~THREAD_FLAGS_KILLED; currentJThread->flags |= THREAD_FLAGS_DYING; assert(blockInts == 1); blockInts = 0; /* this is used to throw a ThreadDeath exception */ onstop(); assert(!"Rescheduling dead thread");}static voidstart_this_sucker_on_a_new_frame(void){ /* all threads start with interrupts turned off */ blockInts = 1; /* I might be dying already */ if ((currentJThread->flags & THREAD_FLAGS_KILLED) != 0) { die(); } intsRestore(); assert(currentJThread->stopCounter == 0); currentJThread->func(currentJThread->jlThread); jthread_exit(); }/* * create a new jthread */jthread *jthread_create(unsigned char pri, void (*func)(void *), int daemon, void *jlThread, size_t threadStackSize){ jthread *jtid; void *oldstack, *newstack;#if defined(__ia64__) void *oldbsp, *newbsp;#endif size_t page_size; /* Adjust stack size */ page_size = getpagesize(); if (threadStackSize == 0) threadStackSize = THREADSTACKSIZE; threadStackSize = (threadStackSize + page_size - 1) & -page_size; /* * Disable stop to protect the threadLock lock, and prevent * the system from losing a new thread context (before the new * thread is queued up). */ jthread_disable_stop(); jmutex_lock(&threadLock); jtid = newThreadCtx(threadStackSize); if (!jtid) { jmutex_unlock(&threadLock); jthread_enable_stop(); return 0; } jtid->priority = pri; jtid->jlThread = jlThread; jtid->status = THREAD_SUSPENDED; jtid->flags = THREAD_FLAGS_GENERAL; jtid->nextlive = liveThreads; liveThreads = jtid; talive++; if ((jtid->daemon = daemon) != 0) { tdaemon++; }DBG(JTHREAD, dprintf("creating thread %p, daemon=%d\n", jtid, daemon); ) jmutex_unlock(&threadLock); assert(func != 0); jtid->func = func; /* * set the first jmp point * * Note that when we return from setjmp in the context of * a new thread, we must no longer access any local variables * in this function. The reason is that we didn't munge * the base pointer that is used to access these variables. * * To be safe, we immediately call a new function. */ if (JTHREAD_SETJMP(JTHREAD_ACCESS_JMPBUF(jtid, env))) { /* new thread */ start_this_sucker_on_a_new_frame(); assert(!"Never!"); /* NOT REACHED */ } #if defined(SAVE_FP) SAVE_FP(jtid->fpstate);#endif /* set up context for new thread */ oldstack = GET_SP(JTHREAD_ACCESS_JMPBUF(jtid, env));#if defined(__ia64__) oldbsp = GET_BSP(JTHREAD_ACCESS_JMPBUF(jtid, env));#endif#if defined(STACK_GROWS_UP)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -