summaryrefslogtreecommitdiff
path: root/rts/Task.h
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/Task.h
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/Task.h')
-rw-r--r--rts/Task.h11
1 files changed, 11 insertions, 0 deletions
diff --git a/rts/Task.h b/rts/Task.h
index 5c7b049631..58798bd24d 100644
--- a/rts/Task.h
+++ b/rts/Task.h
@@ -167,6 +167,17 @@ isBoundTask (Task *task)
return (task->incall->tso != NULL);
}
+// A Task is currently a worker if
+// (a) it was created as a worker (task->worker), and
+// (b) it has not left and re-entered Haskell, in which case
+// task->incall->prev_stack would be non-NULL.
+//
+INLINE_HEADER rtsBool
+isWorker (Task *task)
+{
+ return (task->worker && task->incall->prev_stack == NULL);
+}
+
// Linked list of all tasks.
//
extern Task *all_tasks;