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_PERM_IND(updclosure,heapptr)
29 # define UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
30 UPD_PERM_IND(updclosure,heapptr); and_then
31 # define NOBH_UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
32 NOBH_UPD_PERM_IND(updclosure,heapptr); and_then
35 # define UPD_IND(updclosure, heapptr) \
36 UPD_REAL_IND(updclosure,INFO_PTR(stg_IND_info),heapptr,SEMI)
37 # define UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
38 UPD_REAL_IND(updclosure,ind_info,heapptr,and_then)
39 # define NOBH_UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
40 NOBH_UPD_REAL_IND(updclosure,ind_info,heapptr,and_then)
43 /* These macros have to work in both C and C--, so here's the
49 #define DECLARE_IPTR(info) W_ info
50 #define FCALL foreign "C"
51 #define INFO_PTR(info) info
56 #define DECLARE_IPTR(info) const StgInfoTable *(info)
57 #define FCALL /* nothing */
58 #define INFO_PTR(info) &info
59 #define StgBlockingQueue_blocking_queue(closure) \
60 (((StgBlockingQueue *)closure)->blocking_queue)
61 #define ARG_PTR /* nothing */
64 /* UPD_IND actually does a PERM_IND if TICKY_TICKY is on;
65 if you *really* need an IND use UPD_REAL_IND
67 #define UPD_REAL_IND(updclosure, ind_info, heapptr, and_then) \
70 info = GET_INFO(updclosure); \
71 AWAKEN_BQ(info,updclosure); \
72 updateWithIndirection(GET_INFO(updclosure), ind_info, \
78 #define NOBH_UPD_REAL_IND(updclosure, ind_info, heapptr, and_then) \
80 updateWithIndirection(GET_INFO(updclosure), ind_info, \
86 #if defined(PROFILING) || defined(TICKY_TICKY)
87 #define UPD_PERM_IND(updclosure, heapptr) \
90 info = GET_INFO(updclosure); \
91 AWAKEN_BQ(info,updclosure); \
92 updateWithPermIndirection(info, \
97 #define NOBH_UPD_PERM_IND(updclosure, heapptr) \
99 updateWithPermIndirection(GET_INFO(updclosure), \
105 #if defined(RTS_SUPPORTS_THREADS)
108 # define UPD_IND_NOLOCK(updclosure, heapptr) \
110 DECLARE_IPTR(info); \
111 info = GET_INFO(updclosure); \
112 AWAKEN_BQ_NOLOCK(info,updclosure); \
113 updateWithPermIndirection(info, \
118 # define UPD_IND_NOLOCK(updclosure, heapptr) \
120 DECLARE_IPTR(info); \
121 info = GET_INFO(updclosure); \
122 AWAKEN_BQ_NOLOCK(info,updclosure); \
123 updateWithIndirection(info, INFO_PTR(stg_IND_info), \
130 #define UPD_IND_NOLOCK(updclosure,heapptr) UPD_IND(updclosure,heapptr)
133 /* -----------------------------------------------------------------------------
134 Awaken any threads waiting on a blocking queue (BLACKHOLE_BQ).
135 -------------------------------------------------------------------------- */
140 In a parallel setup several types of closures might have a blocking queue:
141 BLACKHOLE_BQ ... same as in the default concurrent setup; it will be
142 reawakened via calling UPD_IND on that closure after
143 having finished the computation of the graph
144 FETCH_ME_BQ ... a global indirection (FETCH_ME) may be entered by a
145 local TSO, turning it into a FETCH_ME_BQ; it will be
146 reawakened via calling processResume
147 RBH ... a revertible black hole may be entered by another
148 local TSO, putting it onto its blocking queue; since
149 RBHs only exist while the corresponding closure is in
150 transit, they will be reawakened via calling
151 convertToFetchMe (upon processing an ACK message)
153 In a parallel setup a blocking queue may contain 3 types of closures:
154 TSO ... as in the default concurrent setup
155 BLOCKED_FETCH ... indicating that a TSO on another PE is waiting for
156 the result of the current computation
157 CONSTR ... an RBHSave closure (which contains data ripped out of
158 the closure to make room for a blocking queue; since
159 it only contains data we use the exisiting type of
160 a CONSTR closure); this closure is the end of a
161 blocking queue for an RBH closure; it only exists in
162 this kind of blocking queue and must be at the end
165 extern void awakenBlockedQueue(StgBlockingQueueElement *q, StgClosure *node);
166 #define DO_AWAKEN_BQ(bqe, node) STGCALL2(awakenBlockedQueue, bqe, node);
168 #define AWAKEN_BQ(info,closure) \
169 if (info == &stg_BLACKHOLE_BQ_info || \
170 info == &stg_FETCH_ME_BQ_info || \
171 get_itbl(closure)->type == RBH) { \
172 DO_AWAKEN_BQ(((StgBlockingQueue *)closure)->blocking_queue, closure); \
177 extern void awakenBlockedQueue(StgBlockingQueueElement *q, StgClosure *node);
178 #define DO_AWAKEN_BQ(bq, node) STGCALL2(awakenBlockedQueue, bq, node);
180 /* In GranSim we don't have FETCH_ME or FETCH_ME_BQ closures, so they are
181 not checked. The rest of the code is the same as for GUM.
183 #define AWAKEN_BQ(info,closure) \
184 if (info == &stg_BLACKHOLE_BQ_info || \
185 get_itbl(closure)->type == RBH) { \
186 DO_AWAKEN_BQ(((StgBlockingQueue *)closure)->blocking_queue, closure); \
190 #else /* !GRAN && !PAR */
192 #define DO_AWAKEN_BQ(closure) \
193 FCALL awakenBlockedQueue(StgBlockingQueue_blocking_queue(closure) ARG_PTR);
195 #define AWAKEN_BQ(info,closure) \
196 if (info == INFO_PTR(stg_BLACKHOLE_BQ_info)) { \
197 DO_AWAKEN_BQ(closure); \
200 #define AWAKEN_STATIC_BQ(info,closure) \
201 if (info == INFO_PTR(stg_BLACKHOLE_BQ_STATIC_info)) { \
202 DO_AWAKEN_BQ(closure); \
205 #ifdef RTS_SUPPORTS_THREADS
206 #define DO_AWAKEN_BQ_NOLOCK(closure) \
207 FCALL awakenBlockedQueueNoLock(StgBlockingQueue_blocking_queue(closure) ARG_PTR);
209 #define AWAKEN_BQ_NOLOCK(info,closure) \
210 if (info == INFO_PTR(stg_BLACKHOLE_BQ_info)) { \
211 DO_AWAKEN_BQ_NOLOCK(closure); \
214 #endif /* GRAN || PAR */
216 /* -----------------------------------------------------------------------------
217 Updates: lower-level macros which update a closure with an
218 indirection to another closure.
220 There are several variants of this code.
223 -------------------------------------------------------------------------- */
226 * We call LDV_recordDead_FILL_SLOP_DYNAMIC(p1) regardless of the generation in
230 * After all, we do *NOT* need to call LDV_RECORD_CREATE() for both IND and
231 * IND_OLDGEN closures because they are inherently used. But, it corrupts
232 * the invariants that every closure keeps its creation time in the profiling
233 * field. So, we call LDV_RECORD_CREATE().
236 /* In the DEBUG case, we also zero out the slop of the old closure,
237 * so that the sanity checker can tell where the next closure is.
239 * Two important invariants: we should never try to update a closure
240 * to point to itself, and the closure being updated should not
241 * already have been updated (the mutable list will get messed up
246 #define DEBUG_FILL_SLOP(p) /* nothing */
252 #define DEBUG_FILL_SLOP(p) \
257 inf = %GET_STD_INFO(p); \
258 np = TO_W_(%INFO_PTRS(inf)); \
259 nw = TO_W_(%INFO_NPTRS(inf)); \
260 if (%INFO_TYPE(inf) != HALF_W_(THUNK_SELECTOR)) { \
264 StgClosure_payload(p,i) = 0; \
271 #else /* !CMINUSMINUS */
274 DEBUG_FILL_SLOP(StgClosure *p)
276 StgInfoTable *inf = get_itbl(p);
277 nat np = inf->layout.payload.ptrs,
278 nw = inf->layout.payload.nptrs, i;
279 if (inf->type != THUNK_SELECTOR) {
280 for (i = 0; i < np + nw; i++) {
281 ((StgClosure *)p)->payload[i] = 0;
286 #endif /* CMINUSMINUS */
289 /* We have two versions of this macro (sadly), one for use in C-- code,
290 * and the other for C.
292 * The and_then argument is a performance hack so that we can paste in
293 * the continuation code directly. It helps shave a couple of
294 * instructions off the common case in the update code, which is
295 * worthwhile (the update code is often part of the inner loop).
296 * (except that gcc now appears to common up this code again and
297 * invert the optimisation. Grrrr --SDM).
300 #define generation(n) (W_[generations] + n*SIZEOF_generation)
301 #define updateWithIndirection(info, ind_info, p1, p2, and_then) \
304 /* ASSERT( p1 != p2 && !closure_IND(p1) ); \
305 */ LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1); \
307 if (bdescr_gen_no(bd) == 0 :: CInt) { \
308 StgInd_indirectee(p1) = p2; \
309 SET_INFO(p1, ind_info); \
310 LDV_RECORD_CREATE(p1); \
311 TICK_UPD_NEW_IND(); \
314 if (info != stg_BLACKHOLE_BQ_info) { \
315 DEBUG_FILL_SLOP(p1); \
316 foreign "C" recordMutableGen(p1 "ptr", \
317 generation(TO_W_(bdescr_gen_no(bd))) "ptr"); \
319 StgInd_indirectee(p1) = p2; \
320 SET_INFO(p1, stg_IND_OLDGEN_info); \
321 LDV_RECORD_CREATE(p1); \
322 TICK_UPD_OLD_IND(); \
326 #define updateWithIndirection(_info, ind_info, p1, p2, and_then) \
330 ASSERT( (P_)p1 != (P_)p2 && !closure_IND(p1) ); \
331 LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1); \
332 bd = Bdescr((P_)p1); \
333 if (bd->gen_no == 0) { \
334 ((StgInd *)p1)->indirectee = p2; \
335 SET_INFO(p1, ind_info); \
336 LDV_RECORD_CREATE(p1); \
337 TICK_UPD_NEW_IND(); \
340 if (_info != &stg_BLACKHOLE_BQ_info) { \
341 DEBUG_FILL_SLOP(p1); \
342 recordMutableGen(p1, &generations[bd->gen_no]); \
344 ((StgInd *)p1)->indirectee = p2; \
345 SET_INFO(p1, &stg_IND_OLDGEN_info); \
346 TICK_UPD_OLD_IND(); \
352 /* The permanent indirection version isn't performance critical. We
353 * therefore use an inline C function instead of the C-- macro.
357 updateWithPermIndirection(const StgInfoTable *info,
363 ASSERT( p1 != p2 && !closure_IND(p1) );
366 // Destroy the old closure.
367 // Nb: LDV_* stuff cannot mix with ticky-ticky
368 LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);
371 if (bd->gen_no == 0) {
372 ((StgInd *)p1)->indirectee = p2;
373 SET_INFO(p1, &stg_IND_PERM_info);
375 // We have just created a new closure.
376 LDV_RECORD_CREATE(p1);
377 TICK_UPD_NEW_PERM_IND(p1);
379 if (info != &stg_BLACKHOLE_BQ_info) {
380 recordMutableGen(p1, &generations[bd->gen_no]);
382 ((StgInd *)p1)->indirectee = p2;
383 SET_INFO(p1, &stg_IND_OLDGEN_PERM_info);
385 // We have just created a new closure.
386 LDV_RECORD_CREATE(p1);
387 TICK_UPD_OLD_PERM_IND();
392 #endif /* UPDATES_H */