1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team, 1998-2004
7 * ---------------------------------------------------------------------------*/
12 /* -----------------------------------------------------------------------------
15 We have two layers of update macros. The top layer, UPD_IND() and
16 friends perform all the work of an update. In detail:
18 - if the closure being updated is a blocking queue, then all the
19 threads waiting on the blocking queue are updated.
21 - then the lower level updateWithIndirection() macro is invoked
22 to actually replace the closure with an indirection (see below).
24 -------------------------------------------------------------------------- */
27 # define UPD_IND(updclosure, heapptr) \
28 UPD_REAL_IND(updclosure,INFO_PTR(stg_IND_info),heapptr,SEMI)
29 # define UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
30 UPD_REAL_IND(updclosure,ind_info,heapptr,and_then)
32 /* These macros have to work in both C and C--, so here's the
38 #define INFO_PTR(info) info
42 #define INFO_PTR(info) &info
43 #define StgBlockingQueue_blocking_queue(closure) \
44 (((StgBlockingQueue *)closure)->blocking_queue)
47 /* krc: there used to be an UPD_REAL_IND and an
48 UPD_PERM_IND, the latter of which was used for
49 ticky and cost-centre profiling.
50 for now, we just have UPD_REAL_IND. */
51 #define UPD_REAL_IND(updclosure, ind_info, heapptr, and_then) \
53 updateWithIndirection(ind_info, \
59 /* -----------------------------------------------------------------------------
60 Awaken any threads waiting on a blocking queue (BLACKHOLE_BQ).
61 -------------------------------------------------------------------------- */
66 In a parallel setup several types of closures might have a blocking queue:
67 BLACKHOLE_BQ ... same as in the default concurrent setup; it will be
68 reawakened via calling UPD_IND on that closure after
69 having finished the computation of the graph
70 FETCH_ME_BQ ... a global indirection (FETCH_ME) may be entered by a
71 local TSO, turning it into a FETCH_ME_BQ; it will be
72 reawakened via calling processResume
73 RBH ... a revertible black hole may be entered by another
74 local TSO, putting it onto its blocking queue; since
75 RBHs only exist while the corresponding closure is in
76 transit, they will be reawakened via calling
77 convertToFetchMe (upon processing an ACK message)
79 In a parallel setup a blocking queue may contain 3 types of closures:
80 TSO ... as in the default concurrent setup
81 BLOCKED_FETCH ... indicating that a TSO on another PE is waiting for
82 the result of the current computation
83 CONSTR ... an RBHSave closure (which contains data ripped out of
84 the closure to make room for a blocking queue; since
85 it only contains data we use the exisiting type of
86 a CONSTR closure); this closure is the end of a
87 blocking queue for an RBH closure; it only exists in
88 this kind of blocking queue and must be at the end
91 extern void awakenBlockedQueue(StgBlockingQueueElement *q, StgClosure *node);
92 #define DO_AWAKEN_BQ(bqe, node) STGCALL2(awakenBlockedQueue, bqe, node);
94 #define AWAKEN_BQ(info,closure) \
95 if (info == &stg_BLACKHOLE_BQ_info || \
96 info == &stg_FETCH_ME_BQ_info || \
97 get_itbl(closure)->type == RBH) { \
98 DO_AWAKEN_BQ(((StgBlockingQueue *)closure)->blocking_queue, closure); \
103 extern void awakenBlockedQueue(StgBlockingQueueElement *q, StgClosure *node);
104 #define DO_AWAKEN_BQ(bq, node) STGCALL2(awakenBlockedQueue, bq, node);
106 /* In GranSim we don't have FETCH_ME or FETCH_ME_BQ closures, so they are
107 not checked. The rest of the code is the same as for GUM.
109 #define AWAKEN_BQ(info,closure) \
110 if (info == &stg_BLACKHOLE_BQ_info || \
111 get_itbl(closure)->type == RBH) { \
112 DO_AWAKEN_BQ(((StgBlockingQueue *)closure)->blocking_queue, closure); \
115 #endif /* GRAN || PAR */
118 /* -----------------------------------------------------------------------------
119 Updates: lower-level macros which update a closure with an
120 indirection to another closure.
122 There are several variants of this code.
125 -------------------------------------------------------------------------- */
128 * We call LDV_recordDead_FILL_SLOP_DYNAMIC(p1) regardless of the generation in
132 * After all, we do *NOT* need to call LDV_RECORD_CREATE() for both IND and
133 * IND_OLDGEN closures because they are inherently used. But, it corrupts
134 * the invariants that every closure keeps its creation time in the profiling
135 * field. So, we call LDV_RECORD_CREATE().
138 /* In the DEBUG case, we also zero out the slop of the old closure,
139 * so that the sanity checker can tell where the next closure is.
141 * Two important invariants: we should never try to update a closure
142 * to point to itself, and the closure being updated should not
143 * already have been updated (the mutable list will get messed up
146 * NB. We do *not* do this in THREADED_RTS mode, because when we have the
147 * possibility of multiple threads entering the same closure, zeroing
148 * the slop in one of the threads would have a disastrous effect on
149 * the other (seen in the wild!).
153 #define FILL_SLOP(p) \
157 inf = %GET_STD_INFO(p); \
158 if (%INFO_TYPE(inf) != HALF_W_(BLACKHOLE) \
159 && %INFO_TYPE(inf) != HALF_W_(CAF_BLACKHOLE)) { \
160 if (%INFO_TYPE(inf) == HALF_W_(THUNK_SELECTOR)) { \
161 sz = BYTES_TO_WDS(SIZEOF_StgSelector_NoThunkHdr); \
163 if (%INFO_TYPE(inf) == HALF_W_(AP_STACK)) { \
164 sz = StgAP_STACK_size(p) + BYTES_TO_WDS(SIZEOF_StgAP_STACK_NoThunkHdr); \
166 if (%INFO_TYPE(inf) == HALF_W_(AP)) { \
167 sz = TO_W_(StgAP_n_args(p)) + BYTES_TO_WDS(SIZEOF_StgAP_NoThunkHdr); \
169 sz = TO_W_(%INFO_PTRS(inf)) + TO_W_(%INFO_NPTRS(inf)); \
176 StgThunk_payload(p,i) = 0; \
182 #else /* !CMINUSMINUS */
185 FILL_SLOP(StgClosure *p)
187 StgInfoTable *inf = get_itbl(p);
194 // we already filled in the slop when we overwrote the thunk
195 // with BLACKHOLE, and also an evacuated BLACKHOLE is only the
198 sz = sizeofW(StgSelector) - sizeofW(StgThunkHeader);
201 sz = ((StgAP *)p)->n_args + sizeofW(StgAP) - sizeofW(StgThunkHeader);
204 sz = ((StgAP_STACK *)p)->size + sizeofW(StgAP_STACK) - sizeofW(StgThunkHeader);
207 sz = inf->layout.payload.ptrs + inf->layout.payload.nptrs;
210 for (i = 0; i < sz; i++) {
211 ((StgThunk *)p)->payload[i] = 0;
217 #endif /* CMINUSMINUS */
219 #if !defined(DEBUG) || defined(THREADED_RTS)
220 #define DEBUG_FILL_SLOP(p) /* do nothing */
222 #define DEBUG_FILL_SLOP(p) FILL_SLOP(p)
225 /* We have two versions of this macro (sadly), one for use in C-- code,
226 * and the other for C.
228 * The and_then argument is a performance hack so that we can paste in
229 * the continuation code directly. It helps shave a couple of
230 * instructions off the common case in the update code, which is
231 * worthwhile (the update code is often part of the inner loop).
232 * (except that gcc now appears to common up this code again and
233 * invert the optimisation. Grrrr --SDM).
236 #define generation(n) (W_[generations] + n*SIZEOF_generation)
237 #define updateWithIndirection(ind_info, p1, p2, and_then) \
240 DEBUG_FILL_SLOP(p1); \
241 LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1); \
242 StgInd_indirectee(p1) = p2; \
243 prim %write_barrier() []; \
245 if (bdescr_gen_no(bd) != 0 :: CInt) { \
246 recordMutableCap(p1, TO_W_(bdescr_gen_no(bd)), R1); \
247 SET_INFO(p1, stg_IND_OLDGEN_info); \
248 LDV_RECORD_CREATE(p1); \
249 TICK_UPD_OLD_IND(); \
252 SET_INFO(p1, ind_info); \
253 LDV_RECORD_CREATE(p1); \
254 TICK_UPD_NEW_IND(); \
258 #define updateWithIndirection(ind_info, p1, p2, and_then) \
262 ASSERT( (P_)p1 != (P_)p2 ); \
263 /* not necessarily true: ASSERT( !closure_IND(p1) ); */ \
264 /* occurs in RaiseAsync.c:raiseAsync() */ \
265 DEBUG_FILL_SLOP(p1); \
266 LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1); \
267 ((StgInd *)p1)->indirectee = p2; \
269 bd = Bdescr((P_)p1); \
270 if (bd->gen_no != 0) { \
271 recordMutableGenLock(p1, bd->gen_no); \
272 SET_INFO(p1, &stg_IND_OLDGEN_info); \
273 TICK_UPD_OLD_IND(); \
276 SET_INFO(p1, ind_info); \
277 LDV_RECORD_CREATE(p1); \
278 TICK_UPD_NEW_IND(); \
282 #endif /* CMINUSMINUS */
283 #endif /* UPDATES_H */