diff options
author | Simon Marlow <marlowsd@gmail.com> | 2015-06-19 15:12:24 +0100 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2015-06-26 09:32:23 +0100 |
commit | 111ba4beda4ffc48381723da12e5b237d7f9ac59 (patch) | |
tree | 84ec683a20ea340bd60877daa8ab962e5eebaa2e /rts/Schedule.c | |
parent | be0ce8718ea40b091e69dd48fe6bc62b6b551154 (diff) | |
download | haskell-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.c | 14 |
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); |