1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team 1998-2006
5 * Tidying up a thread when it stops running
7 * ---------------------------------------------------------------------------*/
12 #include "RaiseAsync.h"
16 #include <string.h> // for memmove()
18 /* -----------------------------------------------------------------------------
21 * Code largely pinched from old RTS, then hacked to bits. We also do
22 * lazy black holing here.
24 * -------------------------------------------------------------------------- */
26 struct stack_gap { StgWord gap_size; struct stack_gap *next_gap; };
29 stackSqueeze(StgTSO *tso, StgPtr bottom)
32 rtsBool prev_was_update_frame;
33 StgClosure *updatee = NULL;
34 StgRetInfoTable *info;
35 StgWord current_gap_size;
36 struct stack_gap *gap;
39 // Traverse the stack upwards, replacing adjacent update frames
40 // with a single update frame and a "stack gap". A stack gap
41 // contains two values: the size of the gap, and the distance
42 // to the next gap (or the stack top).
46 ASSERT(frame < bottom);
48 prev_was_update_frame = rtsFalse;
50 gap = (struct stack_gap *) (tso->sp - sizeofW(StgUpdateFrame));
52 while (frame < bottom) {
54 info = get_ret_itbl((StgClosure *)frame);
55 switch (info->i.type) {
59 StgUpdateFrame *upd = (StgUpdateFrame *)frame;
61 if (prev_was_update_frame) {
64 /* wasn't there something about update squeezing and ticky to be
65 * sorted out? oh yes: we aren't counting each enter properly
66 * in this case. See the log somewhere. KSW 1999-04-21
68 * Check two things: that the two update frames don't point to
69 * the same object, and that the updatee_bypass isn't already an
70 * indirection. Both of these cases only happen when we're in a
71 * block hole-style loop (and there are multiple update frames
72 * on the stack pointing to the same closure), but they can both
73 * screw us up if we don't check.
75 if (upd->updatee != updatee && !closure_IND(upd->updatee)) {
76 UPD_IND_NOLOCK(upd->updatee, updatee);
79 // now mark this update frame as a stack gap. The gap
80 // marker resides in the bottom-most update frame of
81 // the series of adjacent frames, and covers all the
82 // frames in this series.
83 current_gap_size += sizeofW(StgUpdateFrame);
84 ((struct stack_gap *)frame)->gap_size = current_gap_size;
85 ((struct stack_gap *)frame)->next_gap = gap;
87 frame += sizeofW(StgUpdateFrame);
91 // single update frame, or the topmost update frame in a series
93 prev_was_update_frame = rtsTrue;
94 updatee = upd->updatee;
95 frame += sizeofW(StgUpdateFrame);
101 prev_was_update_frame = rtsFalse;
103 // we're not in a gap... check whether this is the end of a gap
104 // (an update frame can't be the end of a gap).
105 if (current_gap_size != 0) {
106 gap = (struct stack_gap *) (frame - sizeofW(StgUpdateFrame));
108 current_gap_size = 0;
110 frame += stack_frame_sizeW((StgClosure *)frame);
115 if (current_gap_size != 0) {
116 gap = (struct stack_gap *) (frame - sizeofW(StgUpdateFrame));
119 // Now we have a stack with gaps in it, and we have to walk down
120 // shoving the stack up to fill in the gaps. A diagram might
124 // | ********* | <- sp
128 // | stack_gap | <- gap | chunk_size
130 // | ......... | <- gap_end v
136 // 'sp' points the the current top-of-stack
137 // 'gap' points to the stack_gap structure inside the gap
138 // ***** indicates real stack data
139 // ..... indicates gap
140 // <empty> indicates unused
144 void *gap_start, *next_gap_start, *gap_end;
147 next_gap_start = (void *)((unsigned char*)gap + sizeof(StgUpdateFrame));
150 while ((StgPtr)gap > tso->sp) {
152 // we're working in *bytes* now...
153 gap_start = next_gap_start;
154 gap_end = (void*) ((unsigned char*)gap_start - gap->gap_size * sizeof(W_));
157 next_gap_start = (void *)((unsigned char*)gap + sizeof(StgUpdateFrame));
159 chunk_size = (unsigned char*)gap_end - (unsigned char*)next_gap_start;
161 memmove(sp, next_gap_start, chunk_size);
164 tso->sp = (StgPtr)sp;
168 /* -----------------------------------------------------------------------------
171 * We have to prepare for GC - this means doing lazy black holing
172 * here. We also take the opportunity to do stack squeezing if it's
174 * -------------------------------------------------------------------------- */
176 threadPaused(Capability *cap, StgTSO *tso)
179 StgRetInfoTable *info;
182 nat words_to_squeeze = 0;
184 nat weight_pending = 0;
185 rtsBool prev_was_update_frame = rtsFalse;
187 // Check to see whether we have threads waiting to raise
188 // exceptions, and we're not blocking exceptions, or are blocked
189 // interruptibly. This is important; if a thread is running with
190 // TSO_BLOCKEX and becomes blocked interruptibly, this is the only
191 // place we ensure that the blocked_exceptions get a chance.
192 maybePerformBlockedException (cap, tso);
193 if (tso->what_next == ThreadKilled) { return; }
195 stack_end = &tso->stack[tso->stack_size];
197 frame = (StgClosure *)tso->sp;
200 // If we've already marked this frame, then stop here.
201 if (frame->header.info == (StgInfoTable *)&stg_marked_upd_frame_info) {
205 info = get_ret_itbl(frame);
207 switch (info->i.type) {
211 SET_INFO(frame, (StgInfoTable *)&stg_marked_upd_frame_info);
213 bh = ((StgUpdateFrame *)frame)->updatee;
215 if (closure_IND(bh) || bh->header.info == &stg_BLACKHOLE_info) {
216 debugTrace(DEBUG_squeeze,
217 "suspending duplicate work: %ld words of stack",
218 (long)((StgPtr)frame - tso->sp));
220 // If this closure is already an indirection, then
221 // suspend the computation up to this point:
222 suspendComputation(cap,tso,(StgPtr)frame);
224 // Now drop the update frame, and arrange to return
225 // the value to the frame underneath:
226 tso->sp = (StgPtr)frame + sizeofW(StgUpdateFrame) - 2;
227 tso->sp[1] = (StgWord)bh;
228 tso->sp[0] = (W_)&stg_enter_info;
230 // And continue with threadPaused; there might be
231 // yet more computation to suspend.
232 threadPaused(cap,tso);
236 if (bh->header.info != &stg_CAF_BLACKHOLE_info) {
237 #if (!defined(LAZY_BLACKHOLING)) && defined(DEBUG)
238 debugBelch("Unexpected lazy BHing required at 0x%04lx\n",(long)bh);
240 // zero out the slop so that the sanity checker can tell
241 // where the next closure is.
245 // We pretend that bh is now dead.
246 LDV_recordDead_FILL_SLOP_DYNAMIC((StgClosure *)bh);
248 SET_INFO(bh,&stg_BLACKHOLE_info);
250 // We pretend that bh has just been created.
251 LDV_RECORD_CREATE(bh);
254 frame = (StgClosure *) ((StgUpdateFrame *)frame + 1);
255 if (prev_was_update_frame) {
256 words_to_squeeze += sizeofW(StgUpdateFrame);
257 weight += weight_pending;
260 prev_was_update_frame = rtsTrue;
266 // normal stack frames; do nothing except advance the pointer
269 nat frame_size = stack_frame_sizeW(frame);
270 weight_pending += frame_size;
271 frame = (StgClosure *)((StgPtr)frame + frame_size);
272 prev_was_update_frame = rtsFalse;
278 debugTrace(DEBUG_squeeze,
279 "words_to_squeeze: %d, weight: %d, squeeze: %s",
280 words_to_squeeze, weight,
281 weight < words_to_squeeze ? "YES" : "NO");
283 // Should we squeeze or not? Arbitrary heuristic: we squeeze if
284 // the number of words we have to shift down is less than the
285 // number of stack words we squeeze away by doing so.
286 if (RtsFlags.GcFlags.squeezeUpdFrames == rtsTrue &&
287 weight < words_to_squeeze) {
288 stackSqueeze(tso, (StgPtr)frame);