#include "Schedule.h"
#include "Sparks.h"
#include "Trace.h"
+#include "GC.h"
// one global capability, this is the Capability for non-threaded
// builds, and for +RTS -N1
StgClosure *
findSpark (Capability *cap)
{
- /* use the normal Sparks.h interface (internally modified to enable
- concurrent stealing)
- and immediately turn the spark into a thread when successful
- */
Capability *robbed;
StgClosurePtr spark;
rtsBool retry;
nat i = 0;
+ if (!emptyRunQueue(cap)) {
+ // If there are other threads, don't try to run any new
+ // sparks: sparks might be speculative, we don't want to take
+ // resources away from the main computation.
+ return 0;
+ }
+
// first try to get a spark from our own pool.
// We should be using reclaimSpark(), because it works without
// needing any atomic instructions:
cap->no = i;
cap->in_haskell = rtsFalse;
+ cap->in_gc = rtsFalse;
cap->run_queue_hd = END_TSO_QUEUE;
cap->run_queue_tl = END_TSO_QUEUE;
cap->sparks_pruned = 0;
#endif
+ cap->f.stgEagerBlackholeInfo = (W_)&__stg_EAGER_BLACKHOLE_info;
cap->f.stgGCEnter1 = (F_)__stg_gc_enter_1;
cap->f.stgGCFun = (F_)__stg_gc_fun;
cap->mut_lists = stgMallocBytes(sizeof(bdescr *) *
RtsFlags.GcFlags.generations,
"initCapability");
+ cap->saved_mut_lists = stgMallocBytes(sizeof(bdescr *) *
+ RtsFlags.GcFlags.generations,
+ "initCapability");
for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
cap->mut_lists[g] = NULL;
return;
}
- /* if waiting_for_gc was the reason to release the cap: thread
- comes from yieldCap->releaseAndQueueWorker. Unconditionally set
- cap. free and return (see default after the if-protected other
- special cases). Thread will wait on cond.var and re-acquire the
- same cap after GC (GC-triggering cap. calls releaseCap and
- enters the spare_workers case)
- */
- if (waiting_for_gc) {
+ if (waiting_for_gc == PENDING_GC_SEQ) {
last_free_capability = cap; // needed?
trace(TRACE_sched | DEBUG_sched,
"GC pending, set capability %d free", cap->no);
{
Capability *cap = *pCap;
+ if (waiting_for_gc == PENDING_GC_PAR) {
+ debugTrace(DEBUG_sched, "capability %d: becoming a GC thread", cap->no);
+ gcWorkerThread(cap);
+ return;
+ }
+
debugTrace(DEBUG_sched, "giving up capability %d", cap->no);
// We must now release the capability and wait to be woken up
}
/* ----------------------------------------------------------------------------
- * prodCapabilities
+ * prodCapability
*
- * Used to indicate that the interrupted flag is now set, or some
- * other global condition that might require waking up a Task on each
- * Capability.
+ * If a Capability is currently idle, wake up a Task on it. Used to
+ * get every Capability into the GC.
* ------------------------------------------------------------------------- */
-static void
-prodCapabilities(rtsBool all)
-{
- nat i;
- Capability *cap;
- Task *task;
-
- for (i=0; i < n_capabilities; i++) {
- cap = &capabilities[i];
- ACQUIRE_LOCK(&cap->lock);
- if (!cap->running_task) {
- if (cap->spare_workers) {
- trace(TRACE_sched, "resuming capability %d", cap->no);
- task = cap->spare_workers;
- ASSERT(!task->stopped);
- giveCapabilityToTask(cap,task);
- if (!all) {
- RELEASE_LOCK(&cap->lock);
- return;
- }
- }
- }
- RELEASE_LOCK(&cap->lock);
- }
- return;
-}
-
void
-prodAllCapabilities (void)
+prodCapability (Capability *cap, Task *task)
{
- prodCapabilities(rtsTrue);
-}
-
-/* ----------------------------------------------------------------------------
- * prodOneCapability
- *
- * Like prodAllCapabilities, but we only require a single Task to wake
- * up in order to service some global event, such as checking for
- * deadlock after some idle time has passed.
- * ------------------------------------------------------------------------- */
-
-void
-prodOneCapability (void)
-{
- prodCapabilities(rtsFalse);
+ ACQUIRE_LOCK(&cap->lock);
+ if (!cap->running_task) {
+ cap->running_task = task;
+ releaseCapability_(cap,rtsTrue);
+ }
+ RELEASE_LOCK(&cap->lock);
}
/* ----------------------------------------------------------------------------