closure_field(StgTSO, saved_errno);
closure_field(StgTSO, trec);
closure_field(StgTSO, flags);
+ closure_field(StgTSO, dirty);
closure_field_("StgTSO_CCCS", StgTSO, prof.CCCS);
tso_field(StgTSO, sp);
tso_field_offset(StgTSO, stack);
/*
* Flags for the tso->flags field.
- *
- * The TSO_DIRTY flag indicates that this TSO's stack should be
- * scanned during garbage collection. The link field of a TSO is
- * always scanned, so we don't have to dirty a TSO just for linking
- * it on a different list.
- *
- * TSO_DIRTY is set by
- * - schedule(), just before running a thread,
- * - raiseAsync(), because it modifies a thread's stack
- * - resumeThread(), just before running the thread again
- * and unset by the garbage collector (only).
*/
-#define TSO_DIRTY 1
/*
* TSO_LOCKED is set when a TSO is locked to a particular Capability.
*/
typedef StgWord32 StgThreadID;
-#define tsoDirty(tso) ((tso)->flags & TSO_DIRTY)
#define tsoLocked(tso) ((tso)->flags & TSO_LOCKED)
/*
struct StgTSO_* global_link; /* Links all threads together */
+ StgWord dirty; /* non-zero => dirty */
+ /*
+ * The tso->dirty flag indicates that this TSO's stack should be
+ * scanned during garbage collection. It also indicates that this
+ * TSO is on the mutable list.
+ *
+ * NB. The dirty flag gets a word to itself, so that it can be set
+ * safely by multiple threads simultaneously (the flags field is
+ * not safe for this purpose; see #3429). It is harmless for the
+ * TSO to be on the mutable list multiple times.
+ *
+ * tso->dirty is set by dirty_TSO(), and unset by the garbage
+ * collector (only).
+ *
+ * The link field has a separate dirty bit of its own, namely the
+ * bit TSO_LINK_DIRTY in the tso->flags field, set by
+ * setTSOLink().
+ */
+
StgWord16 what_next; /* Values defined in Constants.h */
StgWord16 why_blocked; /* Values defined in Constants.h */
StgWord32 flags;
-------------------------------------------------------------------------- */
+STRING(stg_unblockAsync_err_str, "unblockAsyncExceptions#_ret")
+
INFO_TABLE_RET( stg_unblockAsyncExceptionszh_ret, RET_SMALL )
{
CInt r;
jump %ENTRY_CODE(Sp(0));
}
}
+ else {
+ /*
+ the thread might have been removed from the
+ blocked_exception list by someone else in the meantime.
+ Just restore the stack pointer and continue.
+ */
+ Sp_adj(2);
+ }
}
Sp_adj(1);
tso = StgMVar_head(mvar);
PerformPut(tso,StgMVar_value(mvar));
- if (TO_W_(StgTSO_flags(tso)) & TSO_DIRTY == 0) {
+ if (TO_W_(StgTSO_dirty(tso)) == 0) {
foreign "C" dirty_TSO(MyCapability() "ptr", tso "ptr") [];
}
/* actually perform the putMVar for the thread that we just woke up */
tso = StgMVar_head(mvar);
PerformPut(tso,StgMVar_value(mvar));
- if (TO_W_(StgTSO_flags(tso)) & TSO_DIRTY == 0) {
+ if (TO_W_(StgTSO_dirty(tso)) == 0) {
foreign "C" dirty_TSO(MyCapability() "ptr", tso "ptr") [];
}
/* actually perform the takeMVar */
tso = StgMVar_head(mvar);
PerformTake(tso, val);
- if (TO_W_(StgTSO_flags(tso)) & TSO_DIRTY == 0) {
+ if (TO_W_(StgTSO_dirty(tso)) == 0) {
foreign "C" dirty_TSO(MyCapability() "ptr", tso "ptr") [];
}
/* actually perform the takeMVar */
tso = StgMVar_head(mvar);
PerformTake(tso, R2);
- if (TO_W_(StgTSO_flags(tso)) & TSO_DIRTY == 0) {
+ if (TO_W_(StgTSO_dirty(tso)) == 0) {
foreign "C" dirty_TSO(MyCapability() "ptr", tso "ptr") [];
}
// If this TSO is dirty and in an old generation, it better
// be on the mutable list.
if (tso->what_next == ThreadRelocated) continue;
- if (tso->flags & (TSO_DIRTY|TSO_LINK_DIRTY)) {
+ if (tso->dirty || (tso->flags & TSO_LINK_DIRTY)) {
ASSERT(Bdescr((P_)tso)->gen_no == 0 || (tso->flags & TSO_MARKED));
tso->flags &= ~TSO_MARKED;
}
tso->why_blocked = NotBlocked;
tso->blocked_exceptions = END_TSO_QUEUE;
- tso->flags = TSO_DIRTY;
+ tso->flags = 0;
+ tso->dirty = 1;
tso->saved_errno = 0;
tso->bound = NULL;
default:
printThreadBlockage(t);
}
- if (t->flags & TSO_DIRTY) {
+ if (t->dirty) {
debugBelch(" (TSO_DIRTY)");
} else if (t->flags & TSO_LINK_DIRTY) {
debugBelch(" (TSO_LINK_DIRTY)");
scavenge_stack(tso->sp, &(tso->stack[tso->stack_size]));
if (gct->failed_to_evac) {
- tso->flags |= TSO_DIRTY;
+ tso->dirty = 1;
scavenge_TSO_link(tso);
} else {
- tso->flags &= ~TSO_DIRTY;
+ tso->dirty = 0;
scavenge_TSO_link(tso);
if (gct->failed_to_evac) {
tso->flags |= TSO_LINK_DIRTY;
continue;
case TSO: {
StgTSO *tso = (StgTSO *)p;
- if ((tso->flags & TSO_DIRTY) == 0) {
+ if (tso->dirty == 0) {
// Must be on the mutable list because its link
// field is dirty.
ASSERT(tso->flags & TSO_LINK_DIRTY);
setTSOLink (Capability *cap, StgTSO *tso, StgTSO *target)
{
bdescr *bd;
- if ((tso->flags & (TSO_DIRTY|TSO_LINK_DIRTY)) == 0) {
+ if (tso->dirty == 0 && (tso->flags & TSO_LINK_DIRTY) == 0) {
tso->flags |= TSO_LINK_DIRTY;
bd = Bdescr((StgPtr)tso);
if (bd->gen_no > 0) recordMutableCap((StgClosure*)tso,cap,bd->gen_no);
dirty_TSO (Capability *cap, StgTSO *tso)
{
bdescr *bd;
- if ((tso->flags & (TSO_DIRTY|TSO_LINK_DIRTY)) == 0) {
+ if (tso->dirty == 0 && (tso->flags & TSO_LINK_DIRTY) == 0) {
bd = Bdescr((StgPtr)tso);
if (bd->gen_no > 0) recordMutableCap((StgClosure*)tso,cap,bd->gen_no);
}
- tso->flags |= TSO_DIRTY;
+ tso->dirty = 1;
}
/*