summaryrefslogtreecommitdiff
path: root/rts
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2009-01-07 14:05:07 +0000
committerSimon Marlow <marlowsd@gmail.com>2009-01-07 14:05:07 +0000
commitea1ad23f58b5e45731b47a1d686c0dd73766e1b7 (patch)
treedd73d2f2e1905b23f8f8f766d78b7840f73d2d03 /rts
parent9fd3a4f8760c7e47eb5d4a098559c980d24a9775 (diff)
downloadhaskell-ea1ad23f58b5e45731b47a1d686c0dd73766e1b7.tar.gz
Close the races between throwTo and thread completion
Any threads we missed were being caught by the GC (possibly the idle GC if the system was otherwise inactive), but that's not ideal. The fix (from Bertram Felgenhauer) is to use lockTSO to synchronise, imposing an unconditional lockTSO on thread exit. I couldn't measure any performance overhead from doing this, so it seems reasonable.
Diffstat (limited to 'rts')
-rw-r--r--rts/RaiseAsync.c19
-rw-r--r--rts/Schedule.c12
2 files changed, 20 insertions, 11 deletions
diff --git a/rts/RaiseAsync.c b/rts/RaiseAsync.c
index e65cea3bfd..2f072d551a 100644
--- a/rts/RaiseAsync.c
+++ b/rts/RaiseAsync.c
@@ -264,6 +264,15 @@ check_target:
target = target->_link;
goto retry;
}
+ // check again for ThreadComplete and ThreadKilled. This
+ // cooperates with scheduleHandleThreadFinished to ensure
+ // that we never miss any threads that are throwing an
+ // exception to a thread in the process of terminating.
+ if (target->what_next == ThreadComplete
+ || target->what_next == ThreadKilled) {
+ unlockTSO(target);
+ return THROWTO_SUCCESS;
+ }
blockedThrowTo(cap,source,target);
*out = target;
return THROWTO_BLOCKED;
@@ -564,12 +573,10 @@ maybePerformBlockedException (Capability *cap, StgTSO *tso)
void
awakenBlockedExceptionQueue (Capability *cap, StgTSO *tso)
{
- if (tso->blocked_exceptions != END_TSO_QUEUE) {
- lockTSO(tso);
- awakenBlockedQueue(cap, tso->blocked_exceptions);
- tso->blocked_exceptions = END_TSO_QUEUE;
- unlockTSO(tso);
- }
+ lockTSO(tso);
+ awakenBlockedQueue(cap, tso->blocked_exceptions);
+ tso->blocked_exceptions = END_TSO_QUEUE;
+ unlockTSO(tso);
}
static void
diff --git a/rts/Schedule.c b/rts/Schedule.c
index 9baf755e9f..d22d48fb8f 100644
--- a/rts/Schedule.c
+++ b/rts/Schedule.c
@@ -1403,10 +1403,9 @@ scheduleHandleThreadFinished (Capability *cap STG_UNUSED, Task *task, StgTSO *t)
(unsigned long)t->id, whatNext_strs[t->what_next]);
// blocked exceptions can now complete, even if the thread was in
- // blocked mode (see #2910). The thread is already marked
- // ThreadComplete, so any further throwTos will complete
- // immediately and we don't need to worry about synchronising with
- // those.
+ // blocked mode (see #2910). This unconditionally calls
+ // lockTSO(), which ensures that we don't miss any threads that
+ // are engaged in throwTo() with this thread as a target.
awakenBlockedExceptionQueue (cap, t);
//
@@ -1988,7 +1987,10 @@ resumeThread (void *task_)
debugTrace(DEBUG_sched, "thread %lu: re-entering RTS", (unsigned long)tso->id);
if (tso->why_blocked == BlockedOnCCall) {
- awakenBlockedExceptionQueue(cap,tso);
+ // avoid locking the TSO if we don't have to
+ if (tso->blocked_exceptions != END_TSO_QUEUE) {
+ awakenBlockedExceptionQueue(cap,tso);
+ }
tso->flags &= ~(TSO_BLOCKEX | TSO_INTERRUPTIBLE);
}