summaryrefslogtreecommitdiff
path: root/rts
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2010-12-21 11:58:07 +0000
committerSimon Marlow <marlowsd@gmail.com>2010-12-21 11:58:07 +0000
commit9201a2d104103c2271a03d035555d2bef8a625bb (patch)
tree5ea94e8bccd0f25f20cd7c26a480d98e26e6fedf /rts
parentd8334d807812e40f67770ffc37608c0ce66f96b2 (diff)
downloadhaskell-9201a2d104103c2271a03d035555d2bef8a625bb.tar.gz
boundTaskExiting: don't set task->stopped unless this is the last call (#4850)
The bug in this case was that we had a worker thread making a foreign call which invoked a callback (in this case it was performGC, I think). When the callback ended, boundTaskExiting() was setting task->stopped, but the Task is now per-OS-thread, so it is shared by the worker that made the original foreign call. When the foreign call returned, because task->stopped was set, the worker was not placed on the queue of spare workers. Somehow the worker woke up again, and found the spare_workers queue empty, which lead to a crash. Two bugs here: task->stopped should not have been set by boundTaskExiting (this broke when I split the Task and InCall structs, in 6.12.2), and releaseCapabilityAndQueueWorker() should not be testing task->stopped anyway, because it should only ever be called when task->stopped is false (this is now an assertion).
Diffstat (limited to 'rts')
-rw-r--r--rts/Task.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/rts/Task.c b/rts/Task.c
index a15758c4f1..a5de804418 100644
--- a/rts/Task.c
+++ b/rts/Task.c
@@ -270,8 +270,6 @@ newBoundTask (void)
void
boundTaskExiting (Task *task)
{
- task->stopped = rtsTrue;
-
#if defined(THREADED_RTS)
ASSERT(osThreadId() == task->id);
#endif
@@ -279,6 +277,14 @@ boundTaskExiting (Task *task)
endInCall(task);
+ // Set task->stopped, but only if this is the last call (#4850).
+ // Remember that we might have a worker Task that makes a foreign
+ // call and then a callback, so it can transform into a bound
+ // Task for the duration of the callback.
+ if (task->incall == NULL) {
+ task->stopped = rtsTrue;
+ }
+
debugTrace(DEBUG_sched, "task exiting");
}