X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FSchedule.c;h=b3d34463d39746b91d28eb5ae25787d3e68b4a8d;hb=b77098031265b55b28ddf6f3fe89c429c66cceb7;hp=bce5b2d6dcac9440d3b42e5dc196749541d60c26;hpb=be72dc058ab6bac6a5bf45e5852a4baaf74c2d23;p=ghc-hetmet.git diff --git a/ghc/rts/Schedule.c b/ghc/rts/Schedule.c index bce5b2d..b3d3446 100644 --- a/ghc/rts/Schedule.c +++ b/ghc/rts/Schedule.c @@ -1,5 +1,5 @@ /* --------------------------------------------------------------------------- - * $Id: Schedule.c,v 1.115 2002/02/04 20:40:36 sof Exp $ + * $Id: Schedule.c,v 1.128 2002/02/15 20:58:14 sof Exp $ * * (c) The GHC Team, 1998-2000 * @@ -158,7 +158,7 @@ StgTSO* ActiveTSO = NULL; /* for assigning system costs; GranSim-Light only */ /* rtsTime TimeOfNextEvent, EndOfTimeSlice; now in GranSim.c */ /* - In GranSim we have a runable and a blocked queue for each processor. + In GranSim we have a runnable and a blocked queue for each processor. In order to minimise code changes new arrays run_queue_hds/tls are created. run_queue_hd is then a short cut (macro) for run_queue_hds[CurrentProc] (see GranSim.h). @@ -185,7 +185,9 @@ StgTSO *sleeping_queue; /* perhaps replace with a hash table? */ */ StgTSO *all_threads; -/* Threads suspended in _ccall_GC. +/* When a thread performs a safe C call (_ccall_GC, using old + * terminology), it gets put on the suspended_ccalling_threads + * list. Used by the garbage collector. */ static StgTSO *suspended_ccalling_threads; @@ -263,57 +265,13 @@ static void sched_belch(char *s, ...); */ Mutex sched_mutex = INIT_MUTEX_VAR; Mutex term_mutex = INIT_MUTEX_VAR; -#if defined(THREADED_RTS) -/* - * The rts_mutex is the 'big lock' that the active native - * thread within the RTS holds while executing code - * within the RTS. It is given up when the thread makes a - * transition out of the RTS (e.g., to perform an external - * C call), hopefully for another thread to enter the RTS. - * - */ -Mutex rts_mutex = INIT_MUTEX_VAR; -/* - * When a native thread has completed executing an external - * call, it needs to communicate the result back to the - * (Haskell) thread that made the call. Do this as follows: - * - * - in resumeThread(), the thread increments the counter - * ext_threads_waiting, and then blocks on the - * 'big' RTS lock. - * - upon entry to the scheduler, the thread that's currently - * holding the RTS lock checks ext_threads_waiting. If there - * are native threads waiting, it gives up its RTS lock - * and tries to re-grab the RTS lock [perhaps after having - * waited for a bit..?] - * - care must be taken to deal with the case where more than - * one external thread are waiting on the lock. [ToDo: more] - * - */ - -static nat ext_threads_waiting = 0; -/* - * thread_ready_aux_mutex is used to handle the scenario where the - * the RTS executing thread runs out of work, but there are - * active external threads. The RTS executing thread gives up - * its RTS mutex, and blocks waiting for the thread_ready_cond. - * Unfortunately, a condition variable needs to be associated - * with a mutex in pthreads, so rts_thread_waiting_mutex is - * used for just this purpose. - * - */ -Mutex thread_ready_aux_mutex = INIT_MUTEX_VAR; -#endif - - -/* thread_ready_cond: when signalled, a thread has - * become runnable. When used? - */ -Condition thread_ready_cond = INIT_COND_VAR; -Condition gc_pending_cond = INIT_COND_VAR; +# if defined(SMP) +static Condition gc_pending_cond = INIT_COND_VAR; nat await_death; -#endif +# endif + +#endif /* RTS_SUPPORTS_THREADS */ #if defined(PAR) StgTSO *LastTSO; @@ -355,11 +313,6 @@ static void taskStart(void); static void taskStart(void) { - /* threads start up using 'taskStart', so make them - them grab the RTS lock. */ -#if defined(THREADED_RTS) - ACQUIRE_LOCK(&rts_mutex); -#endif schedule(); } #endif @@ -427,28 +380,17 @@ schedule( void ) rtsBool was_interrupted = rtsFalse; ACQUIRE_LOCK(&sched_mutex); - -#if defined(THREADED_RTS) - /* ToDo: consider SMP support */ - if (ext_threads_waiting > 0) { - /* (At least) one external thread is waiting to - * to deposit the result of an external call. - * Give way to one of them by giving up the RTS - * lock. - */ - RELEASE_LOCK(&sched_mutex); - RELEASE_LOCK(&rts_mutex); - /* ToDo: come up with mechanism that guarantees that - * the main thread doesn't loop here. - */ - yieldThread(); - /* ToDo: longjmp() */ - taskStart(); - } + +#if defined(RTS_SUPPORTS_THREADS) + /* Check to see whether there are any worker threads + waiting to deposit external call results. If so, + yield our capability */ + yieldToReturningWorker(&sched_mutex, cap); + + waitForWorkCapability(&sched_mutex, &cap, rtsFalse); #endif #if defined(GRAN) - /* set up first event to get things going */ /* ToDo: assign costs for system setup and init MainTSO ! */ new_event(CurrentProc, CurrentProc, CurrentTime[CurrentProc], @@ -615,9 +557,8 @@ schedule( void ) * ToDo: what if another client comes along & requests another * main thread? */ - if (blocked_queue_hd != END_TSO_QUEUE || sleeping_queue != END_TSO_QUEUE) { - awaitEvent( - (run_queue_hd == END_TSO_QUEUE) + if ( !EMPTY_QUEUE(blocked_queue_hd) || !EMPTY_QUEUE(sleeping_queue) ) { + awaitEvent( EMPTY_RUN_QUEUE() #if defined(SMP) && allFreeCapabilities() #endif @@ -638,24 +579,28 @@ schedule( void ) * inform all the main threads. */ #ifndef PAR - if (blocked_queue_hd == END_TSO_QUEUE - && run_queue_hd == END_TSO_QUEUE - && sleeping_queue == END_TSO_QUEUE -#if defined(SMP) + if ( EMPTY_RUN_QUEUE() + && EMPTY_QUEUE(blocked_queue_hd) + && EMPTY_QUEUE(sleeping_queue) +#if defined(RTS_SUPPORTS_THREADS) + && EMPTY_QUEUE(suspended_ccalling_threads) +#endif +#ifdef SMP && allFreeCapabilities() -#elif defined(THREADED_RTS) - && suspended_ccalling_threads == END_TSO_QUEUE #endif ) { IF_DEBUG(scheduler, sched_belch("deadlocked, forcing major GC...")); +#if defined(THREADED_RTS) + /* and SMP mode ..? */ + releaseCapability(cap); +#endif RELEASE_LOCK(&sched_mutex); GarbageCollect(GetRoots,rtsTrue); ACQUIRE_LOCK(&sched_mutex); - IF_DEBUG(scheduler, sched_belch("GC done.")); - if (blocked_queue_hd == END_TSO_QUEUE - && run_queue_hd == END_TSO_QUEUE - && sleeping_queue == END_TSO_QUEUE) { + if ( EMPTY_QUEUE(blocked_queue_hd) + && EMPTY_RUN_QUEUE() + && EMPTY_QUEUE(sleeping_queue) ) { IF_DEBUG(scheduler, sched_belch("still deadlocked, checking for black holes...")); detectBlackHoles(); @@ -665,7 +610,7 @@ schedule( void ) * build, send *all* main threads the deadlock exception, * since none of them can make progress). */ - if (run_queue_hd == END_TSO_QUEUE) { + if ( EMPTY_RUN_QUEUE() ) { StgMainThread *m; #if defined(RTS_SUPPORTS_THREADS) for (m = main_threads; m != NULL; m = m->link) { @@ -697,13 +642,14 @@ schedule( void ) #endif } #if defined(RTS_SUPPORTS_THREADS) - if ( run_queue_hd == END_TSO_QUEUE ) { - IF_DEBUG(scheduler, sched_belch("all done, it seems...shut down.")); + /* ToDo: revisit conditions (and mechanism) for shutting + down a multi-threaded world */ + if ( EMPTY_RUN_QUEUE() ) { + IF_DEBUG(scheduler, sched_belch("all done, i think...shutting down.")); shutdownHaskellAndExit(0); - } #endif - ASSERT( run_queue_hd != END_TSO_QUEUE ); + ASSERT( !EMPTY_RUN_QUEUE() ); } } #elif defined(PAR) @@ -720,37 +666,27 @@ schedule( void ) } #endif -#if defined(SMP) +#if defined(RTS_SUPPORTS_THREADS) /* block until we've got a thread on the run queue and a free * capability. + * */ - while ( run_queue_hd == END_TSO_QUEUE - || noFreeCapabilities() - ) { - IF_DEBUG(scheduler, sched_belch("waiting for work")); - waitCondition( &thread_ready_cond, &sched_mutex ); - IF_DEBUG(scheduler, sched_belch("work now available")); + if ( EMPTY_RUN_QUEUE() ) { + /* Give up our capability */ + releaseCapability(cap); + IF_DEBUG(scheduler, sched_belch("thread %d: waiting for work", osThreadId())); + waitForWorkCapability(&sched_mutex, &cap, rtsTrue); + IF_DEBUG(scheduler, sched_belch("thread %d: work now available", osThreadId())); +#if 0 + while ( EMPTY_RUN_QUEUE() ) { + waitForWorkCapability(&sched_mutex, &cap); + IF_DEBUG(scheduler, sched_belch("thread %d: work now available", osThreadId())); + } +#endif } -#elif defined(THREADED_RTS) - if ( run_queue_hd == END_TSO_QUEUE ) { - /* no work available, wait for external calls to complete. */ - IF_DEBUG(scheduler, sched_belch("worker thread (%d): waiting for external thread to complete..", osThreadId())); - RELEASE_LOCK(&sched_mutex); - RELEASE_LOCK(&rts_mutex); - /* Sigh - need to have a mutex locked in order to wait on the - condition variable. */ - ACQUIRE_LOCK(&thread_ready_aux_mutex); - waitCondition(&thread_ready_cond, &thread_ready_aux_mutex); - RELEASE_LOCK(&thread_ready_aux_mutex); - IF_DEBUG(scheduler, sched_belch("worker thread (%d): re-awakened from no-work slumber..\n", osThreadId())); - /* ToDo: longjmp() */ - taskStart(); - - } #endif #if defined(GRAN) - if (RtsFlags.GranFlags.Light) GranSimLight_enter_system(event, &ActiveTSO); // adjust ActiveTSO etc @@ -996,7 +932,7 @@ schedule( void ) belch("--=^ %d threads, %d sparks on [%#x]", run_queue_len(), spark_queue_len(pool), CURRENT_PROC)); -#if 1 +# if 1 if (0 && RtsFlags.ParFlags.ParStats.Full && t && LastTSO && t->id != LastTSO->id && LastTSO->why_blocked == NotBlocked && @@ -1021,11 +957,10 @@ schedule( void ) emitSchedule = rtsFalse; } -#endif +# endif #else /* !GRAN && !PAR */ - /* grab a thread from the run queue - */ + /* grab a thread from the run queue */ ASSERT(run_queue_hd != END_TSO_QUEUE); t = POP_RUN_QUEUE(); // Sanity check the thread we're about to run. This can be @@ -1382,7 +1317,8 @@ schedule( void ) barf("schedule: invalid thread return code %d", (int)ret); } -#ifdef SMP +#if defined(RTS_SUPPORTS_THREADS) + /* I don't understand what this re-grab is doing -- sof */ grabCapability(&cap); #endif @@ -1395,12 +1331,11 @@ schedule( void ) } #endif + if (ready_to_gc #ifdef SMP - if (ready_to_gc && allFreeCapabilities() ) -#else - if (ready_to_gc) + && allFreeCapabilities() #endif - { + ) { /* everybody back, start the GC. * Could do it in this thread, or signal a condition var * to do it in another thread. Either way, we need to @@ -1452,15 +1387,18 @@ schedule( void ) void deleteAllThreads ( void ) { - StgTSO* t; + StgTSO* t, *next; IF_DEBUG(scheduler,sched_belch("deleting all threads")); - for (t = run_queue_hd; t != END_TSO_QUEUE; t = t->link) { + for (t = run_queue_hd; t != END_TSO_QUEUE; t = next) { + next = t->link; deleteThread(t); } - for (t = blocked_queue_hd; t != END_TSO_QUEUE; t = t->link) { + for (t = blocked_queue_hd; t != END_TSO_QUEUE; t = next) { + next = t->link; deleteThread(t); } - for (t = sleeping_queue; t != END_TSO_QUEUE; t = t->link) { + for (t = sleeping_queue; t != END_TSO_QUEUE; t = next) { + next = t->link; deleteThread(t); } run_queue_hd = run_queue_tl = END_TSO_QUEUE; @@ -1509,19 +1447,31 @@ suspendThread( StgRegTable *reg ) cap->r.rCurrentTSO->link = suspended_ccalling_threads; suspended_ccalling_threads = cap->r.rCurrentTSO; +#if defined(RTS_SUPPORTS_THREADS) + cap->r.rCurrentTSO->why_blocked = BlockedOnCCall; +#endif + /* Use the thread ID as the token; it should be unique */ tok = cap->r.rCurrentTSO->id; /* Hand back capability */ - releaseCapability(&cap); + releaseCapability(cap); #if defined(RTS_SUPPORTS_THREADS) && !defined(SMP) - IF_DEBUG(scheduler, sched_belch("thread %d leaving RTS\n", tok)); + /* Preparing to leave the RTS, so ensure there's a native thread/task + waiting to take over. + + ToDo: optimise this and only create a new task if there's a need + for one (i.e., if there's only one Concurrent Haskell thread alive, + there's no need to create a new task). + */ + IF_DEBUG(scheduler, sched_belch("worker thread (%d): leaving RTS", tok)); startTask(taskStart); #endif - + + /* Other threads _might_ be available for execution; signal this */ + THREAD_RUNNABLE(); RELEASE_LOCK(&sched_mutex); - RELEASE_LOCK(&rts_mutex); return tok; } @@ -1531,20 +1481,11 @@ resumeThread( StgInt tok ) StgTSO *tso, **prev; Capability *cap; - IF_DEBUG(scheduler, sched_belch("thread %d returning, waiting for sched. lock.\n", tok)); - ACQUIRE_LOCK(&sched_mutex); - ext_threads_waiting++; - IF_DEBUG(scheduler, sched_belch("thread %d returning, ext_thread count: %d.\n", tok, ext_threads_waiting)); - RELEASE_LOCK(&sched_mutex); - - IF_DEBUG(scheduler, sched_belch("thread %d waiting for RTS lock...\n", tok)); - ACQUIRE_LOCK(&rts_mutex); - ext_threads_waiting--; - IF_DEBUG(scheduler, sched_belch("thread %d acquired RTS lock...\n", tok)); - -#if defined(THREADED_RTS) - /* Free up any RTS-blocked threads. */ - broadcastCondition(&thread_ready_cond); +#if defined(RTS_SUPPORTS_THREADS) + /* Wait for permission to re-enter the RTS with the result. */ + grabReturnCapability(&sched_mutex, &cap); +#else + grabCapability(&cap); #endif /* Remove the thread off of the suspended list */ @@ -1561,19 +1502,12 @@ resumeThread( StgInt tok ) barf("resumeThread: thread not found"); } tso->link = END_TSO_QUEUE; + /* Reset blocking status */ + tso->why_blocked = NotBlocked; -#if defined(SMP) - while ( noFreeCapabilities() ) { - IF_DEBUG(scheduler, sched_belch("waiting to resume")); - waitCondition(&thread_ready_cond, &sched_mutex); - IF_DEBUG(scheduler, sched_belch("resuming thread %d", tso->id)); - } -#endif - - grabCapability(&cap); + RELEASE_LOCK(&sched_mutex); cap->r.rCurrentTSO = tso; - return &cap->r; } @@ -1878,8 +1812,15 @@ activateSpark (rtsSpark spark) * on this thread's stack before the scheduler is invoked. * ------------------------------------------------------------------------ */ +static void scheduleThread_ (StgTSO* tso, rtsBool createTask); + void -scheduleThread(StgTSO *tso) +scheduleThread_(StgTSO *tso + , rtsBool createTask +#if !defined(THREADED_RTS) + STG_UNUSED +#endif + ) { ACQUIRE_LOCK(&sched_mutex); @@ -1889,6 +1830,14 @@ scheduleThread(StgTSO *tso) * soon as we release the scheduler lock below. */ PUSH_ON_RUN_QUEUE(tso); +#if defined(THREADED_RTS) + /* If main() is scheduling a thread, don't bother creating a + * new task. + */ + if ( createTask ) { + startTask(taskStart); + } +#endif THREAD_RUNNABLE(); #if 0 @@ -1897,6 +1846,16 @@ scheduleThread(StgTSO *tso) RELEASE_LOCK(&sched_mutex); } +void scheduleThread(StgTSO* tso) +{ + return scheduleThread_(tso, rtsFalse); +} + +void scheduleExtThread(StgTSO* tso) +{ + return scheduleThread_(tso, rtsTrue); +} + /* --------------------------------------------------------------------------- * initScheduler() * @@ -1954,26 +1913,22 @@ initScheduler(void) #if defined(RTS_SUPPORTS_THREADS) /* Initialise the mutex and condition variables used by * the scheduler. */ - initMutex(&rts_mutex); initMutex(&sched_mutex); initMutex(&term_mutex); -#if defined(THREADED_RTS) - initMutex(&thread_ready_aux_mutex); + + initCondition(&thread_ready_cond); #endif - initCondition(&thread_ready_cond); +#if defined(SMP) initCondition(&gc_pending_cond); #endif -#if defined(THREADED_RTS) - /* Grab big lock */ - ACQUIRE_LOCK(&rts_mutex); - IF_DEBUG(scheduler, - sched_belch("worker thread (%d): acquired RTS lock\n", osThreadId())); +#if defined(RTS_SUPPORTS_THREADS) + ACQUIRE_LOCK(&sched_mutex); #endif /* Install the SIGHUP handler */ -#ifdef SMP +#if defined(SMP) { struct sigaction action,oact; @@ -2004,6 +1959,11 @@ initScheduler(void) #if /* defined(SMP) ||*/ defined(PAR) initSparkPools(); #endif + +#if defined(RTS_SUPPORTS_THREADS) + RELEASE_LOCK(&sched_mutex); +#endif + } void @@ -2058,13 +2018,13 @@ finishAllThreads ( void ) { do { while (run_queue_hd != END_TSO_QUEUE) { - waitThread ( run_queue_hd, NULL ); + waitThread ( run_queue_hd, NULL); } while (blocked_queue_hd != END_TSO_QUEUE) { - waitThread ( blocked_queue_hd, NULL ); + waitThread ( blocked_queue_hd, NULL); } while (sleeping_queue != END_TSO_QUEUE) { - waitThread ( blocked_queue_hd, NULL ); + waitThread ( blocked_queue_hd, NULL); } } while (blocked_queue_hd != END_TSO_QUEUE || @@ -2074,6 +2034,21 @@ finishAllThreads ( void ) SchedulerStatus waitThread(StgTSO *tso, /*out*/StgClosure **ret) +{ +#if defined(THREADED_RTS) + return waitThread_(tso,ret, rtsFalse); +#else + return waitThread_(tso,ret); +#endif +} + +SchedulerStatus +waitThread_(StgTSO *tso, + /*out*/StgClosure **ret +#if defined(THREADED_RTS) + , rtsBool blockWaiting +#endif + ) { StgMainThread *m; SchedulerStatus stat; @@ -2092,13 +2067,27 @@ waitThread(StgTSO *tso, /*out*/StgClosure **ret) m->link = main_threads; main_threads = m; - IF_DEBUG(scheduler, fprintf(stderr, "== scheduler: new main thread (%d)\n", - m->tso->id)); + IF_DEBUG(scheduler, sched_belch("== scheduler: new main thread (%d)\n", m->tso->id)); -#ifdef SMP - do { - waitCondition(&m->wakeup, &sched_mutex); - } while (m->stat == NoStatus); +#if defined(RTS_SUPPORTS_THREADS) + +# if defined(THREADED_RTS) + if (!blockWaiting) { + /* In the threaded case, the OS thread that called main() + * gets to enter the RTS directly without going via another + * task/thread. + */ + RELEASE_LOCK(&sched_mutex); + schedule(); + ASSERT(m->stat != NoStatus); + } else +# endif + { + IF_DEBUG(scheduler, sched_belch("sfoo")); + do { + waitCondition(&m->wakeup, &sched_mutex); + } while (m->stat == NoStatus); + } #elif defined(GRAN) /* GranSim specific init */ CurrentTSO = m->tso; // the TSO to run @@ -2122,7 +2111,10 @@ waitThread(StgTSO *tso, /*out*/StgClosure **ret) m->tso->id)); free(m); - RELEASE_LOCK(&sched_mutex); +#if defined(THREADED_RTS) + if (blockWaiting) +#endif + RELEASE_LOCK(&sched_mutex); return stat; } @@ -3081,51 +3073,39 @@ raiseAsync(StgTSO *tso, StgClosure *exception) StgAP_UPD * ap; /* If we find a CATCH_FRAME, and we've got an exception to raise, - * then build PAP(handler,exception,realworld#), and leave it on - * top of the stack ready to enter. + * then build the THUNK raise(exception), and leave it on + * top of the CATCH_FRAME ready to enter. */ if (get_itbl(su)->type == CATCH_FRAME && exception != NULL) { StgCatchFrame *cf = (StgCatchFrame *)su; + StgClosure *raise; + /* we've got an exception to raise, so let's pass it to the * handler in this frame. */ - ap = (StgAP_UPD *)allocate(sizeofW(StgPAP) + 2); - TICK_ALLOC_UPD_PAP(3,0); - SET_HDR(ap,&stg_PAP_info,cf->header.prof.ccs); - - ap->n_args = 2; - ap->fun = cf->handler; /* :: Exception -> IO a */ - ap->payload[0] = exception; - ap->payload[1] = ARG_TAG(0); /* realworld token */ - - /* throw away the stack from Sp up to and including the - * CATCH_FRAME. - */ - sp = (P_)su + sizeofW(StgCatchFrame) - 1; - tso->su = cf->link; - - /* Restore the blocked/unblocked state for asynchronous exceptions - * at the CATCH_FRAME. - * - * If exceptions were unblocked at the catch, arrange that they - * are unblocked again after executing the handler by pushing an - * unblockAsyncExceptions_ret stack frame. + raise = (StgClosure *)allocate(sizeofW(StgClosure)+1); + TICK_ALLOC_SE_THK(1,0); + SET_HDR(raise,&stg_raise_info,cf->header.prof.ccs); + raise->payload[0] = exception; + + /* throw away the stack from Sp up to the CATCH_FRAME. */ - if (!cf->exceptions_blocked) { - *(sp--) = (W_)&stg_unblockAsyncExceptionszh_ret_info; - } - - /* Ensure that async exceptions are blocked when running the handler. + sp = (P_)su - 1; + + /* Ensure that async excpetions are blocked now, so we don't get + * a surprise exception before we get around to executing the + * handler. */ if (tso->blocked_exceptions == NULL) { - tso->blocked_exceptions = END_TSO_QUEUE; + tso->blocked_exceptions = END_TSO_QUEUE; } - - /* Put the newly-built PAP on top of the stack, ready to execute + + /* Put the newly-built THUNK on top of the stack, ready to execute * when the thread restarts. */ - sp[0] = (W_)ap; + sp[0] = (W_)raise; tso->sp = sp; + tso->su = su; tso->what_next = ThreadEnterGHC; IF_DEBUG(sanity, checkTSO(tso)); return; @@ -3397,6 +3377,11 @@ printThreadBlockage(StgTSO *tso) tso->block_info.closure, info_type(tso->block_info.closure)); break; #endif +#if defined(RTS_SUPPORTS_THREADS) + case BlockedOnCCall: + fprintf(stderr,"is blocked on an external call"); + break; +#endif default: barf("printThreadBlockage: strange tso->why_blocked: %d for TSO %d (%d)", tso->why_blocked, tso->id, tso); @@ -3637,7 +3622,6 @@ sched_belch(char *s, ...) //@subsection Index //@index -//* MainRegTable:: @cindex\s-+MainRegTable //* StgMainThread:: @cindex\s-+StgMainThread //* awaken_blocked_queue:: @cindex\s-+awaken_blocked_queue //* blocked_queue_hd:: @cindex\s-+blocked_queue_hd @@ -3655,5 +3639,4 @@ sched_belch(char *s, ...) //* schedule:: @cindex\s-+schedule //* take_off_run_queue:: @cindex\s-+take_off_run_queue //* term_mutex:: @cindex\s-+term_mutex -//* thread_ready_cond:: @cindex\s-+thread_ready_cond //@end index