rts_evalStableIO: set rtsApiCapability to NULL *before* calling
scheduleWaitThread, matching the way the other eval_* functions do
this.
The previous way lead to a suble race condition:
- thread A calls rts_evalIO, enters scheduleWaitThread()
(rtsApiCapability == NULL).
- thread B calls rts_evalStableIO, creates a main thread and enters
scheduleWaitThread() (rtsApiCapability == &MainCapability)
- thread A exits scheduleWaitThread, sees that rtsApiCapability is
non-NULL, and calls releaseCapability() on it. This is bogus,
because thread A doesn't actually hold the capability, and we've
done a double-release.
This scenario leads to assertion failures in a debug threaded RTS, and
probably crashes in a non-debug threaded RTS.
MERGE TO STABLE
StgTSO* tso;
StgClosure *p, *r;
SchedulerStatus stat;
+ Capability *cap = rtsApiCapability;
+ rtsApiCapability = NULL;
p = (StgClosure *)deRefStablePtr(s);
tso = createStrictIOThread(RtsFlags.GcFlags.initialStkSize, p);
- stat = scheduleWaitThread(tso,&r,rtsApiCapability);
- rtsApiCapability = NULL;
+ stat = scheduleWaitThread(tso,&r,cap);
if (stat == Success && ret != NULL) {
ASSERT(r != NULL);