X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=rts%2FCapability.h;h=3f01bf3d8898af3f86993b486e7479f73a0a3966;hp=dedd6351b693bebc23ba0f0bac3584e29f2f17d5;hb=6cf8982ac30be6836a0cdd8be5a6ac1a1a144213;hpb=ed12b7043fa98928f75c289a756fbcef546315f8 diff --git a/rts/Capability.h b/rts/Capability.h index dedd635..3f01bf3 100644 --- a/rts/Capability.h +++ b/rts/Capability.h @@ -4,27 +4,25 @@ * * Capabilities * - * The notion of a capability is used when operating in multi-threaded - * environments (which the THREADED_RTS build of the RTS does), to - * hold all the state an OS thread/task needs to run Haskell code: - * its STG registers, a pointer to its TSO, a nursery etc. During - * STG execution, a pointer to the capabilitity is kept in a - * register (BaseReg). + * A Capability holds all the state an OS thread/task needs to run + * Haskell code: its STG registers, a pointer to its TSO, a nursery + * etc. During STG execution, a pointer to the Capabilitity is kept in + * a register (BaseReg). * - * Only in an THREADED_RTS build will there be multiple capabilities, - * in the non-threaded builds there is one global capability, namely + * Only in a THREADED_RTS build will there be multiple capabilities, + * in the non-threaded RTS there is one global capability, called * MainCapability. * - * This header file contains the functions for working with capabilities. - * (the main, and only, consumer of this interface is the scheduler). - * * --------------------------------------------------------------------------*/ #ifndef CAPABILITY_H #define CAPABILITY_H -#include "RtsFlags.h" +#include "sm/GC.h" // for evac_fn #include "Task.h" +#include "Sparks.h" + +BEGIN_RTS_PRIVATE struct Capability_ { // State required by the STG virtual machine when running Haskell @@ -45,6 +43,9 @@ struct Capability_ { // catching unsafe call-ins. rtsBool in_haskell; + // true if this Capability is currently in the GC + rtsBool in_gc; + // The run queue. The Task owning this Capability has exclusive // access to its run queue, so can wake up threads without // taking a lock, and the common path through the scheduler is @@ -61,10 +62,16 @@ struct Capability_ { Task *suspended_ccalling_tasks; // One mutable list per generation, so we don't need to take any - // locks when updating an old-generation thunk. These - // mini-mut-lists are moved onto the respective gen->mut_list at - // each GC. + // locks when updating an old-generation thunk. This also lets us + // keep track of which closures this CPU has been mutating, so we + // can traverse them using the right thread during GC and avoid + // unnecessarily moving the data from one cache to another. bdescr **mut_lists; + bdescr **saved_mut_lists; // tmp use during GC + + // Context switch flag. We used to have one global flag, now one + // per capability. Locks required : none (conflicts are harmless) + int context_switch; #if defined(THREADED_RTS) // Worker Tasks waiting in the wings. Singly-linked. @@ -86,6 +93,13 @@ struct Capability_ { // woken up by another Capability. StgTSO *wakeup_queue_hd; StgTSO *wakeup_queue_tl; + + SparkPool *sparks; + + // Stats on spark creation/conversion + nat sparks_created; + nat sparks_converted; + nat sparks_pruned; #endif // Per-capability STM-related data @@ -94,7 +108,13 @@ struct Capability_ { StgTRecChunk *free_trec_chunks; StgTRecHeader *free_trec_headers; nat transaction_tokens; -}; // typedef Capability, defined in RtsAPI.h +} // typedef Capability is defined in RtsAPI.h + // Capabilities are stored in an array, so make sure that adjacent + // Capabilities don't share any cache-lines: +#ifndef mingw32_HOST_OS + ATTRIBUTE_ALIGNED(64) +#endif + ; #if defined(THREADED_RTS) @@ -125,7 +145,7 @@ struct Capability_ { INLINE_HEADER Capability * regTableToCapability (StgRegTable *reg) { - return (Capability *)((void *)((unsigned char*)reg - sizeof(StgFunTable))); + return (Capability *)((void *)((unsigned char*)reg - STG_FIELD_OFFSET(Capability,r))); } // Initialise the available capabilities. @@ -139,22 +159,26 @@ void initCapabilities (void); // ASSUMES: cap->running_task is the current Task. // #if defined(THREADED_RTS) -void releaseCapability (Capability* cap); -void releaseCapability_ (Capability* cap); // assumes cap->lock is held +void releaseCapability (Capability* cap); +void releaseAndWakeupCapability (Capability* cap); +void releaseCapability_ (Capability* cap, rtsBool always_wakeup); +// assumes cap->lock is held #else // releaseCapability() is empty in non-threaded RTS INLINE_HEADER void releaseCapability (Capability* cap STG_UNUSED) {}; -INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED) {}; +INLINE_HEADER void releaseAndWakeupCapability (Capability* cap STG_UNUSED) {}; +INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED, + rtsBool always_wakeup STG_UNUSED) {}; #endif -#if !IN_STG_CODE -// one global capability -extern Capability MainCapability; -#endif +// declared in includes/rts/Threads.h: +// extern Capability MainCapability; + +// declared in includes/rts/Threads.h: +// extern nat n_capabilities; // Array of all the capabilities // -extern nat n_capabilities; extern Capability *capabilities; // The Capability that was last free. Used as a good guess for where @@ -162,6 +186,11 @@ extern Capability *capabilities; // extern Capability *last_free_capability; +// GC indicator, in scope for the scheduler +#define PENDING_GC_SEQ 1 +#define PENDING_GC_PAR 2 +extern volatile StgWord waiting_for_gc; + // Acquires a capability at a return point. If *cap is non-NULL, then // this is taken as a preference for the Capability we wish to // acquire. @@ -199,16 +228,14 @@ void waitForCapability (Task *task, Mutex *mutex, Capability **pCap); // Wakes up a thread on a Capability (probably a different Capability // from the one held by the current Task). // -void wakeupThreadOnCapability (Capability *cap, StgTSO *tso); -void wakeupThreadOnCapability_lock (Capability *cap, StgTSO *tso); - -void migrateThreadToCapability (Capability *cap, StgTSO *tso); -void migrateThreadToCapability_lock (Capability *cap, StgTSO *tso); +void wakeupThreadOnCapability (Capability *my_cap, Capability *other_cap, + StgTSO *tso); // Wakes up a worker thread on just one Capability, used when we // need to service some global event. // void prodOneCapability (void); +void prodCapability (Capability *cap, Task *task); // Similar to prodOneCapability(), but prods all of them. // @@ -217,12 +244,24 @@ void prodAllCapabilities (void); // Waits for a capability to drain of runnable threads and workers, // and then acquires it. Used at shutdown time. // -void shutdownCapability (Capability *cap, Task *task); +void shutdownCapability (Capability *cap, Task *task, rtsBool wait_foreign); // Attempt to gain control of a Capability if it is free. // rtsBool tryGrabCapability (Capability *cap, Task *task); +// Try to find a spark to run +// +StgClosure *findSpark (Capability *cap); + +// True if any capabilities have sparks +// +rtsBool anySparks (void); + +INLINE_HEADER rtsBool emptySparkPoolCap (Capability *cap); +INLINE_HEADER nat sparkPoolSizeCap (Capability *cap); +INLINE_HEADER void discardSparksCap (Capability *cap); + #else // !THREADED_RTS // Grab a capability. (Only in the non-threaded RTS; in the threaded @@ -232,8 +271,18 @@ extern void grabCapability (Capability **pCap); #endif /* !THREADED_RTS */ -// Free a capability on exit -void freeCapability (Capability *cap); +// cause all capabilities to context switch as soon as possible. +void setContextSwitches(void); +INLINE_HEADER void contextSwitchCapability(Capability *cap); + +// Free all capabilities +void freeCapabilities (void); + +// For the GC: +void markSomeCapabilities (evac_fn evac, void *user, nat i0, nat delta, + rtsBool prune_sparks); +void markCapabilities (evac_fn evac, void *user); +void traverseSparkQueues (evac_fn evac, void *user); /* ----------------------------------------------------------------------------- * INLINE functions... private below here @@ -244,6 +293,8 @@ recordMutableCap (StgClosure *p, Capability *cap, nat gen) { bdescr *bd; + // We must own this Capability in order to modify its mutable list. + ASSERT(cap->running_task == myTask()); bd = cap->mut_lists[gen]; if (bd->free >= bd->start + BLOCK_SIZE_W) { bdescr *new_bd; @@ -255,4 +306,32 @@ recordMutableCap (StgClosure *p, Capability *cap, nat gen) *bd->free++ = (StgWord)p; } +#if defined(THREADED_RTS) +INLINE_HEADER rtsBool +emptySparkPoolCap (Capability *cap) +{ return looksEmpty(cap->sparks); } + +INLINE_HEADER nat +sparkPoolSizeCap (Capability *cap) +{ return sparkPoolSize(cap->sparks); } + +INLINE_HEADER void +discardSparksCap (Capability *cap) +{ return discardSparks(cap->sparks); } +#endif + +INLINE_HEADER void +contextSwitchCapability (Capability *cap) +{ + // setting HpLim to NULL ensures that the next heap check will + // fail, and the thread will return to the scheduler. + cap->r.rHpLim = NULL; + // But just in case it didn't work (the target thread might be + // modifying HpLim at the same time), we set the end-of-block + // context-switch flag too: + cap->context_switch = 1; +} + +END_RTS_PRIVATE + #endif /* CAPABILITY_H */