*
* ---------------------------------------------------------------------------*/
+// #include "PosixSource.h"
#include "Rts.h"
-#include "Storage.h"
-#include "LdvProfile.h"
+
+#include "ThreadPaused.h"
+#include "sm/Storage.h"
#include "Updates.h"
#include "RaiseAsync.h"
#include "Trace.h"
-#include "RtsFlags.h"
+#include "Threads.h"
#include <string.h> // for memmove()
struct stack_gap { StgWord gap_size; struct stack_gap *next_gap; };
static void
-stackSqueeze(StgTSO *tso, StgPtr bottom)
+stackSqueeze(Capability *cap, StgTSO *tso, StgPtr bottom)
{
StgPtr frame;
rtsBool prev_was_update_frame;
// contains two values: the size of the gap, and the distance
// to the next gap (or the stack top).
- frame = tso->sp;
+ frame = tso->stackobj->sp;
ASSERT(frame < bottom);
prev_was_update_frame = rtsFalse;
current_gap_size = 0;
- gap = (struct stack_gap *) (tso->sp - sizeofW(StgUpdateFrame));
+ gap = (struct stack_gap *) (frame - sizeofW(StgUpdateFrame));
while (frame <= bottom) {
* screw us up if we don't check.
*/
if (upd->updatee != updatee && !closure_IND(upd->updatee)) {
- UPD_IND(upd->updatee, updatee);
+ updateThunk(cap, tso, upd->updatee, updatee);
}
// now mark this update frame as a stack gap. The gap
// <empty> indicates unused
//
{
- void *sp;
- void *gap_start, *next_gap_start, *gap_end;
+ StgWord8 *sp;
+ StgWord8 *gap_start, *next_gap_start, *gap_end;
nat chunk_size;
- next_gap_start = (void *)((unsigned char*)gap + sizeof(StgUpdateFrame));
+ next_gap_start = (StgWord8*)gap + sizeof(StgUpdateFrame);
sp = next_gap_start;
- while ((StgPtr)gap > tso->sp) {
+ while ((StgPtr)gap > tso->stackobj->sp) {
// we're working in *bytes* now...
gap_start = next_gap_start;
- gap_end = (void*) ((unsigned char*)gap_start - gap->gap_size * sizeof(W_));
+ gap_end = gap_start - gap->gap_size * sizeof(W_);
gap = gap->next_gap;
- next_gap_start = (void *)((unsigned char*)gap + sizeof(StgUpdateFrame));
+ next_gap_start = (StgWord8*)gap + sizeof(StgUpdateFrame);
- chunk_size = (unsigned char*)gap_end - (unsigned char*)next_gap_start;
+ chunk_size = gap_end - next_gap_start;
sp -= chunk_size;
memmove(sp, next_gap_start, chunk_size);
}
- tso->sp = (StgPtr)sp;
+ tso->stackobj->sp = (StgPtr)sp;
}
}
maybePerformBlockedException (cap, tso);
if (tso->what_next == ThreadKilled) { return; }
- // NB. Blackholing is *not* optional, we must either do lazy
+ // NB. Blackholing is *compulsory*, we must either do lazy
// blackholing, or eager blackholing consistently. See Note
// [upd-black-hole] in sm/Scav.c.
- stack_end = &tso->stack[tso->stack_size];
+ stack_end = tso->stackobj->stack + tso->stackobj->stack_size;
- frame = (StgClosure *)tso->sp;
-
- while (1) {
- // If we've already marked this frame, then stop here.
- if (frame->header.info == (StgInfoTable *)&stg_marked_upd_frame_info) {
- if (prev_was_update_frame) {
- words_to_squeeze += sizeofW(StgUpdateFrame);
- weight += weight_pending;
- weight_pending = 0;
- }
- goto end;
- }
+ frame = (StgClosure *)tso->stackobj->sp;
- info = get_ret_itbl(frame);
+ while ((P_)frame < stack_end) {
+ info = get_ret_itbl(frame);
switch (info->i.type) {
-
+
case UPDATE_FRAME:
+ // If we've already marked this frame, then stop here.
+ if (frame->header.info == (StgInfoTable *)&stg_marked_upd_frame_info) {
+ if (prev_was_update_frame) {
+ words_to_squeeze += sizeofW(StgUpdateFrame);
+ weight += weight_pending;
+ weight_pending = 0;
+ }
+ goto end;
+ }
+
SET_INFO(frame, (StgInfoTable *)&stg_marked_upd_frame_info);
bh = ((StgUpdateFrame *)frame)->updatee;
#ifdef THREADED_RTS
retry:
#endif
- if (closure_flags[INFO_PTR_TO_STRUCT(bh_info)->type] & _IND
- || bh_info == &stg_BLACKHOLE_info) {
+ // If the info table is a WHITEHOLE or a BLACKHOLE, then
+ // another thread has claimed it (via the SET_INFO()
+ // below), or is in the process of doing so. In that case
+ // we want to suspend the work that the current thread has
+ // done on this thunk and wait until the other thread has
+ // finished.
+ //
+ // If eager blackholing is taking place, it could be the
+ // case that the blackhole points to the current
+ // TSO. e.g.:
+ //
+ // this thread other thread
+ // --------------------------------------------------------
+ // c->indirectee = other_tso;
+ // c->header.info = EAGER_BH
+ // threadPaused()
+ // c->indirectee = other_tso;
+ // c->header.info = EAGER_BH
+ // c->header.info = BLACKHOLE
+ // threadPaused()
+ // *** c->header.info is now BLACKHOLE,
+ // c->indirectee points to this TSO
+ //
+ // So in this case do *not* suspend the work of the
+ // current thread, because the current thread will become
+ // deadlocked on itself. See #5226 for an instance of
+ // this bug.
+ //
+ if ((bh_info == &stg_WHITEHOLE_info ||
+ bh_info == &stg_BLACKHOLE_info)
+ &&
+ ((StgInd*)bh)->indirectee != (StgClosure*)tso)
+ {
debugTrace(DEBUG_squeeze,
"suspending duplicate work: %ld words of stack",
- (long)((StgPtr)frame - tso->sp));
+ (long)((StgPtr)frame - tso->stackobj->sp));
// If this closure is already an indirection, then
// suspend the computation up to this point.
// Now drop the update frame, and arrange to return
// the value to the frame underneath:
- tso->sp = (StgPtr)frame + sizeofW(StgUpdateFrame) - 2;
- tso->sp[1] = (StgWord)bh;
- tso->sp[0] = (W_)&stg_enter_info;
+ tso->stackobj->sp = (StgPtr)frame + sizeofW(StgUpdateFrame) - 2;
+ tso->stackobj->sp[1] = (StgWord)bh;
+ ASSERT(bh->header.info != &stg_TSO_info);
+ tso->stackobj->sp[0] = (W_)&stg_enter_info;
// And continue with threadPaused; there might be
// yet more computation to suspend.
- frame = (StgClosure *)tso->sp + 2;
+ frame = (StgClosure *)(tso->stackobj->sp + 2);
prev_was_update_frame = rtsFalse;
continue;
}
- if (bh->header.info != &stg_CAF_BLACKHOLE_info) {
- // zero out the slop so that the sanity checker can tell
- // where the next closure is.
- DEBUG_FILL_SLOP(bh);
-#ifdef PROFILING
- // @LDV profiling
- // We pretend that bh is now dead.
- LDV_recordDead_FILL_SLOP_DYNAMIC((StgClosure *)bh);
-#endif
- // an EAGER_BLACKHOLE gets turned into a BLACKHOLE here.
+
+ // zero out the slop so that the sanity checker can tell
+ // where the next closure is.
+ OVERWRITING_CLOSURE(bh);
+
+ // an EAGER_BLACKHOLE or CAF_BLACKHOLE gets turned into a
+ // BLACKHOLE here.
#ifdef THREADED_RTS
- cur_bh_info = (const StgInfoTable *)
- cas((StgVolatilePtr)&bh->header.info,
- (StgWord)bh_info,
- (StgWord)&stg_BLACKHOLE_info);
-
- if (cur_bh_info != bh_info) {
- bh_info = cur_bh_info;
- goto retry;
- }
-#else
- SET_INFO(bh,&stg_BLACKHOLE_info);
+ // first we turn it into a WHITEHOLE to claim it, and if
+ // successful we write our TSO and then the BLACKHOLE info pointer.
+ cur_bh_info = (const StgInfoTable *)
+ cas((StgVolatilePtr)&bh->header.info,
+ (StgWord)bh_info,
+ (StgWord)&stg_WHITEHOLE_info);
+
+ if (cur_bh_info != bh_info) {
+ bh_info = cur_bh_info;
+ goto retry;
+ }
#endif
- // We pretend that bh has just been created.
- LDV_RECORD_CREATE(bh);
- }
+ // The payload of the BLACKHOLE points to the TSO
+ ((StgInd *)bh)->indirectee = (StgClosure *)tso;
+ write_barrier();
+ SET_INFO(bh,&stg_BLACKHOLE_info);
+
+ // .. and we need a write barrier, since we just mutated the closure:
+ recordClosureMutated(cap,bh);
+
+ // We pretend that bh has just been created.
+ LDV_RECORD_CREATE(bh);
frame = (StgClosure *) ((StgUpdateFrame *)frame + 1);
if (prev_was_update_frame) {
prev_was_update_frame = rtsTrue;
break;
- case STOP_FRAME:
+ case UNDERFLOW_FRAME:
+ case STOP_FRAME:
goto end;
// normal stack frames; do nothing except advance the pointer
// the number of words we have to shift down is less than the
// number of stack words we squeeze away by doing so.
if (RtsFlags.GcFlags.squeezeUpdFrames == rtsTrue &&
- ((weight <= 5 && words_to_squeeze > 0) || weight < words_to_squeeze)) {
- stackSqueeze(tso, (StgPtr)frame);
+ ((weight <= 8 && words_to_squeeze > 0) || weight < words_to_squeeze)) {
+ // threshold above bumped from 5 to 8 as a result of #2797
+ stackSqueeze(cap, tso, (StgPtr)frame);
+ tso->flags |= TSO_SQUEEZED;
+ // This flag tells threadStackOverflow() that the stack was
+ // squeezed, because it may not need to be expanded.
+ } else {
+ tso->flags &= ~TSO_SQUEEZED;
}
}