[commit: ghc] master: fix race condition in yieldCapability() (#5552) (f9f0b08)
Simon Marlow
marlowsd at gmail.com
Mon Oct 24 16:10:29 CEST 2011
Repository : ssh://darcs.haskell.org//srv/darcs/ghc
On branch : master
http://hackage.haskell.org/trac/ghc/changeset/f9f0b08750af311190830f6d4de270806fe52789
>---------------------------------------------------------------
commit f9f0b08750af311190830f6d4de270806fe52789
Author: Simon Marlow <marlowsd at gmail.com>
Date: Mon Oct 24 13:29:32 2011 +0100
fix race condition in yieldCapability() (#5552)
See comment for details. I've tried quite hard, but haven't been able
to make a small test case that reproduces the bug.
>---------------------------------------------------------------
rts/Capability.c | 27 ++++++++++++++++++++++++++-
1 files changed, 26 insertions(+), 1 deletions(-)
diff --git a/rts/Capability.c b/rts/Capability.c
index 57c75e6..7bba58c 100644
--- a/rts/Capability.c
+++ b/rts/Capability.c
@@ -653,7 +653,15 @@ yieldCapability (Capability** pCap, Task *task)
continue;
}
- if (task->incall->tso == NULL) {
+ if (task->cap != cap) {
+ // see Note [migrated bound threads]
+ debugTrace(DEBUG_sched,
+ "task has been migrated to cap %d", task->cap->no);
+ RELEASE_LOCK(&cap->lock);
+ continue;
+ }
+
+ if (task->incall->tso == NULL) {
ASSERT(cap->spare_workers != NULL);
// if we're not at the front of the queue, release it
// again. This is unlikely to happen.
@@ -681,6 +689,23 @@ yieldCapability (Capability** pCap, Task *task)
return;
}
+// Note [migrated bound threads]
+//
+// There's a tricky case where:
+// - cap A is running an unbound thread T1
+// - there is a bound thread T2 at the head of the run queue on cap A
+// - T1 makes a safe foreign call, the task bound to T2 is woken up on cap A
+// - T1 returns quickly grabbing A again (T2 is still waking up on A)
+// - T1 blocks, the scheduler migrates T2 to cap B
+// - the task bound to T2 wakes up on cap B
+//
+// We take advantage of the following invariant:
+//
+// - A bound thread can only be migrated by the holder of the
+// Capability on which the bound thread currently lives. So, if we
+// hold Capabilty C, and task->cap == C, then task cannot be
+// migrated under our feet.
+
/* ----------------------------------------------------------------------------
* prodCapability
*
More information about the Cvs-ghc
mailing list