X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FCapability.c;h=143eefe432c6660516b808919e31083e99177e68;hb=5638488ba28ec84fbf64bf2742a040e3fa30bed4;hp=5ca2f518fe0782db77d23cf224e0154ec275208a;hpb=c282a8106fc20dcd96025f7aca52fb69cd0ac670;p=ghc-hetmet.git diff --git a/ghc/rts/Capability.c b/ghc/rts/Capability.c index 5ca2f51..143eefe 100644 --- a/ghc/rts/Capability.c +++ b/ghc/rts/Capability.c @@ -1,6 +1,6 @@ /* --------------------------------------------------------------------------- * - * (c) The GHC Team, 2003-2005 + * (c) The GHC Team, 2003-2006 * * Capabilities * @@ -10,9 +10,9 @@ * STG execution, a pointer to the capabilitity is kept in a * register (BaseReg; actually it is a pointer to cap->r). * - * Only in an SMP build will there be multiple capabilities, for - * the threaded RTS and other non-threaded builds, there is only - * one global capability, namely MainCapability. + * Only in an THREADED_RTS build will there be multiple capabilities, + * for non-threaded builds there is only one global capability, namely + * MainCapability. * * --------------------------------------------------------------------------*/ @@ -26,9 +26,9 @@ #include "Schedule.h" #include "Sparks.h" -#if !defined(SMP) -Capability MainCapability; // for non-SMP, we have one global capability -#endif +// one global capability, this is the Capability for non-threaded +// builds, and for +RTS -N1 +Capability MainCapability; nat n_capabilities; Capability *capabilities = NULL; @@ -39,47 +39,38 @@ Capability *capabilities = NULL; // locking, so we don't do that. Capability *last_free_capability; -#ifdef SMP -#define UNUSED_IF_NOT_SMP -#else -#define UNUSED_IF_NOT_SMP STG_UNUSED -#endif - -#ifdef RTS_USER_SIGNALS -#define UNUSED_IF_NOT_THREADS -#else -#define UNUSED_IF_NOT_THREADS STG_UNUSED -#endif - - +#if defined(THREADED_RTS) STATIC_INLINE rtsBool globalWorkToDo (void) { return blackholes_need_checking - || interrupted -#if defined(RTS_USER_SIGNALS) - || signals_pending() -#endif + || sched_state >= SCHED_INTERRUPTING ; } +#endif #if defined(THREADED_RTS) STATIC_INLINE rtsBool anyWorkForMe( Capability *cap, Task *task ) { - // If the run queue is not empty, then we only wake up the guy who - // can run the thread at the head, even if there is some other - // reason for this task to run (eg. interrupted=rtsTrue). - if (!emptyRunQueue(cap)) { - if (cap->run_queue_hd->bound == NULL) { - return (task->tso == NULL); - } else { - return (cap->run_queue_hd->bound == task); - } - } else if (task->tso == NULL && !emptySparkPoolCap(cap)) { - return rtsTrue; + if (task->tso != NULL) { + // A bound task only runs if its thread is on the run queue of + // the capability on which it was woken up. Otherwise, we + // can't be sure that we have the right capability: the thread + // might be woken up on some other capability, and task->cap + // could change under our feet. + return !emptyRunQueue(cap) && cap->run_queue_hd->bound == task; + } else { + // A vanilla worker task runs if either there is a lightweight + // thread at the head of the run queue, or the run queue is + // empty and (there are sparks to execute, or there is some + // other global condition to check, such as threads blocked on + // blackholes). + if (emptyRunQueue(cap)) { + return !emptySparkPoolCap(cap) || globalWorkToDo(); + } else + return cap->run_queue_hd->bound == NULL; } - return globalWorkToDo(); } #endif @@ -166,7 +157,7 @@ initCapability( Capability *cap, nat i ) /* --------------------------------------------------------------------------- * Function: initCapabilities() * - * Purpose: set up the Capability handling. For the SMP build, + * Purpose: set up the Capability handling. For the THREADED_RTS build, * we keep a table of them, the size of which is * controlled by the user via the RTS flag -N. * @@ -174,21 +165,42 @@ initCapability( Capability *cap, nat i ) void initCapabilities( void ) { -#if defined(SMP) - nat i,n; +#if defined(THREADED_RTS) + nat i; + +#ifndef REG_Base + // We can't support multiple CPUs if BaseReg is not a register + if (RtsFlags.ParFlags.nNodes > 1) { + errorBelch("warning: multiple CPUs not supported in this build, reverting to 1"); + RtsFlags.ParFlags.nNodes = 1; + } +#endif - n_capabilities = n = RtsFlags.ParFlags.nNodes; - capabilities = stgMallocBytes(n * sizeof(Capability), "initCapabilities"); + n_capabilities = RtsFlags.ParFlags.nNodes; - for (i = 0; i < n; i++) { + if (n_capabilities == 1) { + capabilities = &MainCapability; + // THREADED_RTS must work on builds that don't have a mutable + // BaseReg (eg. unregisterised), so in this case + // capabilities[0] must coincide with &MainCapability. + } else { + capabilities = stgMallocBytes(n_capabilities * sizeof(Capability), + "initCapabilities"); + } + + for (i = 0; i < n_capabilities; i++) { initCapability(&capabilities[i], i); } - IF_DEBUG(scheduler, sched_belch("allocated %d capabilities", n)); -#else + IF_DEBUG(scheduler, sched_belch("allocated %d capabilities", + n_capabilities)); + +#else /* !THREADED_RTS */ + n_capabilities = 1; capabilities = &MainCapability; initCapability(&MainCapability, 0); + #endif // There are no free capabilities to begin with. We will start @@ -213,7 +225,7 @@ initCapabilities( void ) #if defined(THREADED_RTS) STATIC_INLINE void -giveCapabilityToTask (Capability *cap, Task *task) +giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task) { ASSERT_LOCK_HELD(&cap->lock); ASSERT(task->cap == cap); @@ -269,20 +281,12 @@ releaseCapability_ (Capability* cap) return; } - // If we have an unbound thread on the run queue, or if there's - // anything else to do, give the Capability to a worker thread. - if (!emptyRunQueue(cap) || !emptySparkPoolCap(cap) || globalWorkToDo()) { - if (cap->spare_workers) { - giveCapabilityToTask(cap,cap->spare_workers); - // The worker Task pops itself from the queue; - return; - } - + if (!cap->spare_workers) { // Create a worker thread if we don't have one. If the system // is interrupted, we only create a worker task if there // are threads that need to be completed. If the system is // shutting down, we never create a new worker. - if (!shutting_down_scheduler) { + if (sched_state < SCHED_SHUTTING_DOWN || !emptyRunQueue(cap)) { IF_DEBUG(scheduler, sched_belch("starting new worker on capability %d", cap->no)); startWorkerTask(cap, workerStart); @@ -290,12 +294,22 @@ releaseCapability_ (Capability* cap) } } + // If we have an unbound thread on the run queue, or if there's + // anything else to do, give the Capability to a worker thread. + if (!emptyRunQueue(cap) || !emptySparkPoolCap(cap) || globalWorkToDo()) { + if (cap->spare_workers) { + giveCapabilityToTask(cap,cap->spare_workers); + // The worker Task pops itself from the queue; + return; + } + } + last_free_capability = cap; IF_DEBUG(scheduler, sched_belch("freeing capability %d", cap->no)); } void -releaseCapability (Capability* cap UNUSED_IF_NOT_THREADS) +releaseCapability (Capability* cap USED_IF_THREADS) { ACQUIRE_LOCK(&cap->lock); releaseCapability_(cap); @@ -303,7 +317,7 @@ releaseCapability (Capability* cap UNUSED_IF_NOT_THREADS) } static void -releaseCapabilityAndQueueWorker (Capability* cap UNUSED_IF_NOT_THREADS) +releaseCapabilityAndQueueWorker (Capability* cap USED_IF_THREADS) { Task *task; @@ -341,8 +355,7 @@ releaseCapabilityAndQueueWorker (Capability* cap UNUSED_IF_NOT_THREADS) * * ------------------------------------------------------------------------- */ void -waitForReturnCapability (Capability **pCap, - Task *task UNUSED_IF_NOT_THREADS) +waitForReturnCapability (Capability **pCap, Task *task) { #if !defined(THREADED_RTS) @@ -519,6 +532,7 @@ prodCapabilities(rtsBool all) } RELEASE_LOCK(&cap->lock); } + return; } void @@ -561,7 +575,7 @@ shutdownCapability (Capability *cap, Task *task) { nat i; - ASSERT(interrupted && shutting_down_scheduler); + ASSERT(sched_state == SCHED_SHUTTING_DOWN); task->cap = cap;