2 % (c) The AQUA Project, Glasgow University, 1994
4 %************************************************************************
6 \section[StgThreads.lhc]{Threaded Threads Support}
8 %************************************************************************
10 Some of the threads support is done in threaded code. How's that for ambiguous
17 #define MAIN_REG_MAP /* STG world */
22 #include "Statistics.h"
28 %************************************************************************
30 \subsection[thread-objects]{Special objects for thread support}
32 %************************************************************************
34 TSO's are Thread State Objects, where the thread context is stored when the
35 thread is sleeping, and where we have slots for STG registers that don't
36 live in real machine registers.
46 fprintf(stderr, "TSO Entry: panic");
53 Stack objects are chunks of stack words allocated out of the heap and
54 linked together in a chain.
64 fprintf(stderr, "StkO Entry: panic");
74 STGFUN(StkO_static_entry)
78 fprintf(stderr, "StkO_static Entry: panic");
88 Blocking queues are essentially black holes with threads attached. These
89 are the threads to be awakened when the closure is updated.
93 EXTFUN(EnterNodeCode);
100 STGCALL0(void,(),GranSimBlock); /* Before overwriting TSO_LINK */
103 TSO_LINK(CurrentTSO) = (P_) BQ_ENTRIES(Node);
104 BQ_ENTRIES(Node) = (W_) CurrentTSO;
106 LivenessReg = LIVENESS_R1;
108 TSO_PC1(CurrentTSO) = EnterNodeCode;
111 QP_Event1("GR", CurrentTSO);
114 if(RTSflags.ParFlags.granSimStats) {
115 /* Note that CURRENT_TIME may perform an unsafe call */
116 TIME now = CURRENT_TIME;
117 TSO_EXECTIME(CurrentTSO) += now - TSO_BLOCKEDAT(CurrentTSO);
118 TSO_BLOCKCOUNT(CurrentTSO)++;
119 TSO_QUEUE(CurrentTSO) = Q_BLOCKED;
120 TSO_BLOCKEDAT(CurrentTSO) = now;
121 DumpGranEvent(GR_BLOCK, CurrentTSO);
125 ReSchedule(NEW_THREAD);
136 Revertible black holes are needed in the parallel world, to handle
137 negative acknowledgements of messages containing updatable closures.
138 The idea is that when the original message is transmitted, the closure
139 is turned into a revertible black hole...an object which acts like a
140 black hole when local threads try to enter it, but which can be
141 reverted back to the original closure if necessary.
143 It's actually a lot like a blocking queue (BQ) entry, because
144 revertible black holes are initially set up with an empty blocking
147 The combination of GrAnSim with revertible black holes has not been
159 STGCALL0(void, (), GranSimBlock); /* Before overwriting TSO_LINK */
162 switch (INFO_TYPE(InfoPtr)) {
163 case INFO_SPEC_RBH_TYPE:
164 TSO_LINK(CurrentTSO) = (P_) SPEC_RBH_BQ(Node);
165 SPEC_RBH_BQ(Node) = (W_) CurrentTSO;
167 case INFO_GEN_RBH_TYPE:
168 TSO_LINK(CurrentTSO) = (P_) GEN_RBH_BQ(Node);
169 GEN_RBH_BQ(Node) = (W_) CurrentTSO;
173 fprintf(stderr, "Panic: non-{SPEC,GEN} RBH %#lx (IP %#lx)\n", Node, InfoPtr);
177 LivenessReg = LIVENESS_R1;
179 TSO_PC1(CurrentTSO) = EnterNodeCode;
182 QP_Event1("GR", CurrentTSO);
185 if(RTSflags.ParFlags.granSimStats) {
186 /* Note that CURRENT_TIME may perform an unsafe call */
187 TIME now = CURRENT_TIME;
188 TSO_EXECTIME(CurrentTSO) += now - TSO_BLOCKEDAT(CurrentTSO);
189 TSO_BLOCKCOUNT(CurrentTSO)++;
190 TSO_QUEUE(CurrentTSO) = Q_BLOCKED;
191 TSO_BLOCKEDAT(CurrentTSO) = now;
192 DumpGranEvent(GR_BLOCK, CurrentTSO);
196 ReSchedule(NEW_THREAD);
208 %************************************************************************
210 \subsection[thread-entrypoints]{Scheduler-Thread Interfaces}
212 %************************************************************************
214 The normal way of entering a thread is through \tr{resumeThread},
215 which short-circuits any indirections to the TSO and StkO, sets up STG
216 registers, and jumps to the saved PC.
223 while(IS_INDIRECTION(INFO_PTR(CurrentTSO))) {
224 CurrentTSO = (P_) IND_CLOSURE_PTR(CurrentTSO);
228 if (RTSflags.ParFlags.granSimStats) {
229 TSO_QUEUE(CurrentTSO) = Q_RUNNING;
230 /* Note that CURRENT_TIME may perform an unsafe call */
231 TSO_BLOCKEDAT(CurrentTSO) = CURRENT_TIME;
235 CurrentRegTable = TSO_INTERNAL_PTR(CurrentTSO);
237 while(IS_INDIRECTION(INFO_PTR(SAVE_StkO))) {
238 SAVE_StkO = (P_) IND_CLOSURE_PTR(SAVE_StkO);
242 SET_TASK_ACTIVITY(ST_REDUCING);
243 RESTORE_CCC(TSO_CCC(CurrentTSO));
244 JMP_(TSO_PC1(CurrentTSO));
249 Since we normally context switch during a heap check, it is possible
250 that we will return to a previously suspended thread without
251 sufficient heap for the thread to continue. However, we have cleverly
252 stashed away the heap requirements in @TSO_ARG1@ so that we can decide
253 whether or not to perform a garbage collection before resuming the
254 thread. The actual thread resumption address (either @EnterNodeCode@
255 or elsewhere) is stashed in @TSO_PC2@.
258 STGFUN(CheckHeapCode)
262 ALLOC_HEAP(TSO_ARG1(CurrentTSO)); /* ticky profiling */
263 if ((Hp += TSO_ARG1(CurrentTSO)) > HpLim) {
264 ReallyPerformThreadGC(TSO_ARG1(CurrentTSO), rtsFalse);
267 SET_TASK_ACTIVITY(ST_REDUCING);
268 RESUME_(TSO_PC2(CurrentTSO));
273 Often, a thread starts (or rather, resumes) by entering the closure
274 that Node points to. Here's a tiny code fragment to do just that.
275 The saved PC in the TSO can be set to @EnterNodeCode@ whenever we
276 want this to happen upon resumption of the thread.
279 STGFUN(EnterNodeCode)
283 InfoPtr=(D_)(INFO_PTR(Node));
284 GRAN_EXEC(5,1,2,0,0);
285 JMP_(ENTRY_CODE(InfoPtr));
290 Then, there are the occasions when we just want to pick up where we
291 left off. We use \tr{RESUME_} here instead of \tr{JMP_}, because when
292 we return to a call site, the Alpha is going to try to load \tr{%gp}
293 from \tr{%ra} rather than \tr{%pv}, and \tr{JMP_} only sets \tr{%pv}.
294 Resuming to the start of a function is currently okay, but an
295 extremely bad practice. As we add support for more architectures, we
296 can expect the difference between \tr{RESUME_} and \tr{JMP_} to become
304 SET_TASK_ACTIVITY(ST_REDUCING);
305 RESUME_(TSO_PC2(CurrentTSO));
310 %************************************************************************
312 \subsection[stack-chunk-underflow-code]{Underflow code for stack chunks}
314 %************************************************************************
320 On a uniprocessor, stack underflow causes us no great headaches. The
321 old value of RetReg is squirreled away at the base of the top stack
322 object (the one that's about to get blown away). We just yank it
323 outta there and perform the same kind of return that got us here in
326 This simplicity is due to the fact that we never have to fetch a stack
331 #define DO_RETURN_TEMPLATE(label, cont) \
336 temp = STKO_LINK(StkOReg); \
337 RetReg = STKO_RETURN(StkOReg); \
339 RestoreStackStgRegs(); \
344 DO_RETURN_TEMPLATE(UnderflowDirectReturn, DIRECT(((P_)RetReg)))
345 DO_RETURN_TEMPLATE(UnderflowVect0, ((P_)RetReg)[RVREL(0)])
346 DO_RETURN_TEMPLATE(UnderflowVect1, ((P_)RetReg)[RVREL(1)])
347 DO_RETURN_TEMPLATE(UnderflowVect2, ((P_)RetReg)[RVREL(2)])
348 DO_RETURN_TEMPLATE(UnderflowVect3, ((P_)RetReg)[RVREL(3)])
349 DO_RETURN_TEMPLATE(UnderflowVect4, ((P_)RetReg)[RVREL(4)])
351 DO_RETURN_TEMPLATE(UnderflowVect5, ((P_)RetReg)[RVREL(5)])
352 DO_RETURN_TEMPLATE(UnderflowVect6, ((P_)RetReg)[RVREL(6)])
353 DO_RETURN_TEMPLATE(UnderflowVect7, ((P_)RetReg)[RVREL(7)])
355 DO_RETURN_TEMPLATE(StackUnderflowEnterNode, EnterNodeCode)
361 In the parallel world, we may have to fetch the StkO from a remote
362 location before we can load up the stack registers and perform the
363 return. Our convention is that we load RetReg up with the exact
364 continuation address (after a vector table lookup, if necessary),
365 and tail-call the code to fetch the stack object. (Of course, if
366 the stack object is already local, we then just jump to the
367 continuation address.)
371 STGFUN(CommonUnderflow)
376 temp = STKO_LINK(StkOReg);
378 /* fprintf(stderr,"Stk Underflow from: %lx to: %lx size abandoned: %d\n",StkOReg,temp,STKO_CLOSURE_CTS_SIZE(StkOReg)); */
380 /* change the guy we are abandoning into something
381 that will not be "interesting" on the mutables
382 list. (As long as it is there, it will be
383 scavenged in GC, and we cannot guarantee that
384 it is still a "sane" StkO object). (And, besides,
385 why continue to keep it [and all it pts to] alive?)
388 FREEZE_MUT_HDR(StkOReg, ImMutArrayOfPtrs_info);
389 MUTUPLE_CLOSURE_SIZE(StkOReg) = MUTUPLE_VHS;
392 /* ToDo: Fetch the remote stack object here! */
393 RestoreStackStgRegs();
398 #define DO_RETURN_TEMPLATE(label, cont) \
402 RetReg = STKO_RETURN(StkOReg); \
403 RetReg = (StgRetAddr)(cont); \
404 LivenessReg = INFO_LIVENESS(InfoPtr); \
405 JMP_(CommonUnderflow); \
409 DO_RETURN_TEMPLATE(UnderflowDirectReturn, DIRECT(((P_)RetReg)))
410 DO_RETURN_TEMPLATE(UnderflowVect0, ((P_)RetReg)[RVREL(0)])
411 DO_RETURN_TEMPLATE(UnderflowVect1, ((P_)RetReg)[RVREL(1)])
412 DO_RETURN_TEMPLATE(UnderflowVect2, ((P_)RetReg)[RVREL(2)])
413 DO_RETURN_TEMPLATE(UnderflowVect3, ((P_)RetReg)[RVREL(3)])
414 DO_RETURN_TEMPLATE(UnderflowVect4, ((P_)RetReg)[RVREL(4)])
415 DO_RETURN_TEMPLATE(UnderflowVect5, ((P_)RetReg)[RVREL(5)])
416 DO_RETURN_TEMPLATE(UnderflowVect6, ((P_)RetReg)[RVREL(6)])
417 DO_RETURN_TEMPLATE(UnderflowVect7, ((P_)RetReg)[RVREL(7)])
419 STGFUN(PrimUnderflow)
422 RetReg = STKO_RETURN(StkOReg);
423 RetReg = (StgRetAddr)DIRECT(((P_)RetReg));
424 LivenessReg = NO_LIVENESS;
425 JMP_(CommonUnderflow);
430 * This one is similar, but isn't part of the return vector. It's only used
431 * when we fall off of a stack chunk and want to enter Node rather than
432 * returning through RetReg. (This occurs during UpdatePAP, when the updatee
433 * isn't on the current stack chunk.) It can't be done with the template,
434 * because R2 is dead, and R1 points to a PAP. Only R1 is live.
437 STGFUN(StackUnderflowEnterNode)
440 RetReg = (StgRetAddr)(EnterNodeCode);
441 LivenessReg = LIVENESS_R1;
442 JMP_(CommonUnderflow);
450 /* "MAX_VECTORED_RTN" elements (see GhcConstants.lh) */
465 IFN_(seqDirectReturn) {
469 RetReg = (StgRetAddr) SpB[BREL(0)];
470 cont = (void *) SpB[BREL(1)];
472 /* GRAN_EXEC(1,1,2,0,0); /? ToDo: RE-CHECK (WDP) */
478 NB: For direct returns to work properly, the name of the routine must be
479 the same as the name of the vector table with vtbl_ removed and DirectReturn
480 appended. This is all the mangler understands.
485 (W_) seqDirectReturn,
486 (W_) seqDirectReturn,
487 (W_) seqDirectReturn,
488 (W_) seqDirectReturn,
489 (W_) seqDirectReturn,
490 (W_) seqDirectReturn,
491 (W_) seqDirectReturn,
495 #endif /* CONCURRENT */