summaryrefslogtreecommitdiff
path: root/rts/Schedule.c
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2015-06-19 15:12:24 +0100
committerSimon Marlow <marlowsd@gmail.com>2015-06-26 09:32:23 +0100
commit111ba4beda4ffc48381723da12e5b237d7f9ac59 (patch)
tree84ec683a20ea340bd60877daa8ab962e5eebaa2e /rts/Schedule.c
parentbe0ce8718ea40b091e69dd48fe6bc62b6b551154 (diff)
downloadhaskell-111ba4beda4ffc48381723da12e5b237d7f9ac59.tar.gz
Fix deadlock (#10545)
yieldCapability() was not prepared to be called by a Task that is not either a worker or a bound Task. This could happen if we ended up in yieldCapability via this call stack: performGC() scheduleDoGC() requestSync() yieldCapability() and there were a few other ways this could happen via requestSync. The fix is to handle this case in yieldCapability(): when the Task is not a worker or a bound Task, we put it on the returning_workers queue, where it will be woken up again. Summary of changes: * `yieldCapability`: factored out subroutine waitForWorkerCapability` * `waitForReturnCapability` renamed to `waitForCapability`, and factored out subroutine `waitForReturnCapability` * `releaseCapabilityAndQueue` worker renamed to `enqueueWorker`, does not take a lock and no longer tests if `!isBoundTask()` * `yieldCapability` adjusted for refactorings, only change in behavior is when it is not a worker or bound task. Test Plan: * new test concurrent/should_run/performGC * validate Reviewers: niteria, austin, ezyang, bgamari Subscribers: thomie, bgamari Differential Revision: https://phabricator.haskell.org/D997 GHC Trac Issues: #10545
Diffstat (limited to 'rts/Schedule.c')
-rw-r--r--rts/Schedule.c14
1 files changed, 7 insertions, 7 deletions
diff --git a/rts/Schedule.c b/rts/Schedule.c
index f81fc0e703..6edb7d063e 100644
--- a/rts/Schedule.c
+++ b/rts/Schedule.c
@@ -1424,7 +1424,7 @@ static void acquireAllCapabilities(Capability *cap, Task *task)
// all the Capabilities, but even so it's a slightly
// unsavoury invariant.
task->cap = tmpcap;
- waitForReturnCapability(&tmpcap, task);
+ waitForCapability(&tmpcap, task);
if (tmpcap->no != i) {
barf("acquireAllCapabilities: got the wrong capability");
}
@@ -1801,7 +1801,7 @@ forkProcess(HsStablePtr *entry
task = newBoundTask();
cap = NULL;
- waitForReturnCapability(&cap, task);
+ waitForCapability(&cap, task);
#ifdef THREADED_RTS
do {
@@ -2278,7 +2278,7 @@ resumeThread (void *task_)
task->cap = cap;
// Wait for permission to re-enter the RTS with the result.
- waitForReturnCapability(&cap,task);
+ waitForCapability(&cap,task);
// we might be on a different capability now... but if so, our
// entry on the suspended_ccalls list will also have been
// migrated.
@@ -2408,7 +2408,7 @@ void scheduleWorker (Capability *cap, Task *task)
// cap->lock until we've finished workerTaskStop() below.
//
// There may be workers still involved in foreign calls; those
- // will just block in waitForReturnCapability() because the
+ // will just block in waitForCapability() because the
// Capability has been shut down.
//
ACQUIRE_LOCK(&cap->lock);
@@ -2499,7 +2499,7 @@ exitScheduler (rtsBool wait_foreign USED_IF_THREADS)
if (sched_state < SCHED_SHUTTING_DOWN) {
sched_state = SCHED_INTERRUPTING;
Capability *cap = task->cap;
- waitForReturnCapability(&cap,task);
+ waitForCapability(&cap,task);
scheduleDoGC(&cap,task,rtsTrue);
ASSERT(task->incall->tso == NULL);
releaseCapability(cap);
@@ -2523,7 +2523,7 @@ freeScheduler( void )
still_running = freeTaskManager();
// We can only free the Capabilities if there are no Tasks still
// running. We might have a Task about to return from a foreign
- // call into waitForReturnCapability(), for example (actually,
+ // call into waitForCapability(), for example (actually,
// this should be the *only* thing that a still-running Task can
// do at this point, and it will block waiting for the
// Capability).
@@ -2567,7 +2567,7 @@ performGC_(rtsBool force_major)
// TODO: do we need to traceTask*() here?
- waitForReturnCapability(&cap,task);
+ waitForCapability(&cap,task);
scheduleDoGC(&cap,task,force_major);
releaseCapability(cap);
boundTaskExiting(task);