1 \section[PerformGC]{Wrapper for heap overflow}
7 @PerformGC@ is the wrapper for calls to @collectHeap@ in the
8 storage manager. It performs the following actions:
10 \item Save live registers.
11 \item If black holing is required before garbage collection we must
12 black hole the update frames on the B stack and any live registers
13 pointing at updatable closures --- possibly R1, if live and in update? --JSM
14 \item Call the garbage collector.
15 \item Restore registers.
17 They either succeed or crash-and-burn; hence, they don't return
20 @PerformGC@ saves the fixed STG registers. and calls the garbage
21 collector. It also black holes the B Stack if this is required at
22 garbage collection time.
24 There's also a function @PerformGCIO@ which does all the above and is
25 used to force a full collection.
28 #if defined(CONCURRENT)
29 EXTFUN(EnterNodeCode); /* For reentering node after GC */
30 EXTFUN(CheckHeapCode); /* For returning to thread after a context switch */
31 extern P_ AvailableStack;
33 EXTDATA_RO(FetchMe_info);
36 static void BlackHoleUpdateStack(STG_NO_ARGS);
37 #endif /* CONCURRENT */
39 extern smInfo StorageMgrInfo;
40 extern void PrintRednCountInfo(STG_NO_ARGS);
41 extern I_ showRednCountStats;
42 extern I_ SM_word_heap_size;
43 extern I_ squeeze_upd_frames;
45 #if defined(GRAN_CHECK) && defined(GRAN)
49 extern FILE *main_statsfile; /* Might be of general interest HWL */
52 /* the real work is done by this function --- see wrappers at end */
55 RealPerformGC(liveness, reqsize, always_reenter_node, do_full_collection)
58 W_ always_reenter_node;
59 rtsBool do_full_collection;
61 I_ num_ptr_roots = 0; /* we bump this counter as we
62 store roots; de-bump it
63 as we re-store them. */
64 #if defined(USE_COST_CENTRES)
68 /* stop the profiling timer --------------------- */
69 #if defined(USE_COST_CENTRES)
70 /* STOP_TIME_PROFILER; */
75 SAVE_Liveness = liveness;
78 Even on a uniprocessor, we may have to reenter node after a
79 context switch. Though it can't turn into a FetchMe, its shape
80 may have changed (e.g. from a thunk to a data object).
82 if (always_reenter_node) {
83 /* Avoid infinite loops at the same heap check */
84 if (SAVE_Hp <= SAVE_HpLim && TSO_SWITCH(CurrentTSO) == TSO_PC2(CurrentTSO)) {
85 TSO_SWITCH(CurrentTSO) = NULL;
88 /* Set up to re-enter Node, so as to be sure it's really there. */
89 assert(liveness & LIVENESS_R1);
90 TSO_SWITCH(CurrentTSO) = TSO_PC2(CurrentTSO);
91 TSO_PC2(CurrentTSO) = EnterNodeCode;
96 if (context_switch && !do_full_collection
97 # if defined(USE_COST_CENTRES)
101 /* We're in a GC callWrapper, so the thread state is safe */
102 TSO_ARG1(CurrentTSO) = reqsize;
103 TSO_PC1(CurrentTSO) = CheckHeapCode;
106 TSO_EXECTIME(CurrentTSO) += CURRENT_TIME - TSO_BLOCKEDAT(CurrentTSO);
110 ReSchedule(9 /*i.e. error; was SAME_THREAD*/);
116 /* Don't use SET_CCC, because we don't want to bump the sub_scc_count */
117 # if defined(USE_COST_CENTRES)
120 CCC = (CostCentre)STATIC_CC_REF(CC_GC);
123 ReallyPerformThreadGC(reqsize, do_full_collection);
125 #else /* !CONCURRENT */
127 # if defined(USE_COST_CENTRES)
128 /* Don't use SET_CCC, because we don't want to bump the sub_scc_count */
130 CCC = (CostCentre)STATIC_CC_REF(CC_GC);
134 /* root saving ---------------------------------- */
136 # define __ENROOT_PTR_REG(cond,n) /* n == 1 <=> R1 */ \
138 StorageMgrInfo.roots[num_ptr_roots] = CAT2(MAIN_R,n).p; \
142 __ENROOT_PTR_REG(IS_LIVE_R1(liveness),1);
143 __ENROOT_PTR_REG(IS_LIVE_R2(liveness),2);
144 __ENROOT_PTR_REG(IS_LIVE_R3(liveness),3);
145 __ENROOT_PTR_REG(IS_LIVE_R4(liveness),4);
146 __ENROOT_PTR_REG(IS_LIVE_R5(liveness),5);
147 __ENROOT_PTR_REG(IS_LIVE_R6(liveness),6);
148 __ENROOT_PTR_REG(IS_LIVE_R7(liveness),7);
149 __ENROOT_PTR_REG(IS_LIVE_R8(liveness),8);
152 * Before we garbage collect we may have to squeeze update frames and/or
153 * black hole the update stack
155 if (squeeze_upd_frames) {
156 /* Squeeze and/or black hole update frames */
159 displacement = SqueezeUpdateFrames(stackInfo.botB + BREL(1), MAIN_SpB, MAIN_SuB);
161 MAIN_SuB += BREL(displacement);
162 MAIN_SpB += BREL(displacement);
163 /* fprintf(stderr, "B size %d, squeezed out %d\n", MAIN_SpB - stackInfo.botB,
165 } /* note the conditional else clause below */
166 # if defined(SM_DO_BH_UPDATE)
168 BlackHoleUpdateStack();
169 # endif /* SM_DO_BH_UPDATE */
171 assert(num_ptr_roots <= SM_MAXROOTS);
172 StorageMgrInfo.rootno = num_ptr_roots;
175 /* Move (SAVE_)Hp back to where it was */
176 /* (heap is known to grow upwards) */
177 /* we *do* have to do this, so reported stats will be right! */
179 /* the main business ---------------------------- */
186 /* Restore hpLim to its "correct" setting */
187 StorageMgrInfo.hplim += StorageMgrInfo.hardHpOverflowSize;
189 GC_result = collectHeap(reqsize, &StorageMgrInfo, do_full_collection);
191 if ( GC_result == GC_HARD_LIMIT_EXCEEDED ) {
192 OutOfHeapHook(reqsize * sizeof(W_), SM_word_heap_size * sizeof(W_)); /*msg*/
196 } else if ( GC_result == GC_SOFT_LIMIT_EXCEEDED ) {
197 /* Allow ourselves to use emergency space */
198 /* Set hplim so that we'll GC when we hit the soft limit */
199 StorageMgrInfo.hplim -= StorageMgrInfo.hardHpOverflowSize;
200 raiseError( softHeapOverflowHandler );
202 } else if ( GC_result == GC_SUCCESS ) {
203 /* Set hplim so that we'll GC when we hit the soft limit */
204 StorageMgrInfo.hplim -= StorageMgrInfo.hardHpOverflowSize;
206 } else { /* This should not happen */
207 fprintf(stderr, "Panic: garbage collector returned %d please report it as a bug to glasgow-haskell-bugs@dcs.gla.ac.uk\n", GC_result );
209 # if defined(DO_REDN_COUNTING)
210 if (showRednCountStats) {
211 PrintRednCountInfo();
218 StorageMgrInfo.rootno = 0; /* reset */
221 /* Semantics of GC ensures that a block of
222 `reqsize' is now available (and allocated) [NB: sequential only] */
224 /* root restoring ------------------------------- */
225 /* must do all the restoring exactly backwards to the storing! */
227 /* now the general regs, in *backwards* order */
229 # define __DEROOT_PTR_REG(cond,n) /* n == 1 <=> R1 */ \
232 CAT2(MAIN_R,n).p = StorageMgrInfo.roots[num_ptr_roots]; \
235 __DEROOT_PTR_REG(IS_LIVE_R8(liveness),8);
236 __DEROOT_PTR_REG(IS_LIVE_R7(liveness),7);
237 __DEROOT_PTR_REG(IS_LIVE_R6(liveness),6);
238 __DEROOT_PTR_REG(IS_LIVE_R5(liveness),5);
239 __DEROOT_PTR_REG(IS_LIVE_R4(liveness),4);
240 __DEROOT_PTR_REG(IS_LIVE_R3(liveness),3);
241 __DEROOT_PTR_REG(IS_LIVE_R2(liveness),2);
242 __DEROOT_PTR_REG(IS_LIVE_R1(liveness),1);
244 assert(num_ptr_roots == 0); /* we have put it all back */
246 unblockUserSignals();
248 #endif /* !CONCURRENT */
250 #if defined(USE_COST_CENTRES)
253 RESTART_TIME_PROFILER;
258 This is a wrapper used for all standard, non-threaded, non-parallel GC
261 #ifdef HEAP_CHK_HYGIENE
262 I_ doHygieneCheck = 0;
269 W_ liveness = HEAP_OVERFLOW_LIVENESS(args);
270 W_ reqsize = HEAP_OVERFLOW_REQSIZE(args);
271 W_ always_reenter_node = HEAP_OVERFLOW_REENTER(args);
273 #ifdef HEAP_CHK_HYGIENE
274 if (doHygieneCheck) {
279 RealPerformGC(liveness, reqsize, always_reenter_node, rtsFalse);
282 #if defined(CONCURRENT) && defined(GRAN)
283 /* This is directly called from the macro GRAN_RESCHEDULE out of the */
284 /* threaded world. -- HWL */
287 PerformReschedule(liveness, always_reenter_node)
289 W_ always_reenter_node;
292 I_ need_to_reschedule;
294 /* Reset the global NeedToReSchedule --
295 this is used only to communicate the fact that we should schedule
296 a new thread rather than the existing one following a fetch.
298 need_to_reschedule = NeedToReSchedule;
299 NeedToReSchedule = rtsFalse;
301 SAVE_Liveness = liveness;
303 if (always_reenter_node) {
304 /* Avoid infinite loops at the same context switch */
305 if ((TSO_SWITCH(CurrentTSO) == TSO_PC2(CurrentTSO)) &&
306 !need_to_reschedule) {
307 TSO_SWITCH(CurrentTSO) = NULL;
311 /* Set up to re-enter Node, so as to be sure it's really there. */
312 assert(liveness & LIVENESS_R1);
313 TSO_SWITCH(CurrentTSO) = TSO_PC2(CurrentTSO);
314 TSO_PC2(CurrentTSO) = (void *) EnterNodeCode;
317 /* We're in a GC callWrapper, so the thread state is safe */
318 TSO_ARG1(CurrentTSO) = 0;
319 TSO_PC1(CurrentTSO) = EnterNodeCode;
320 ReSchedule( (need_to_reschedule && !DoReScheduleOnFetch) ?
321 CHANGE_THREAD : SAME_THREAD );
326 /* this is a wrapper used when we want to do a full GC.
328 One reason might be that we're about to enter a time-critical piece
329 of code and want to reduce the risk of a GC during the run. The
330 motivating reason is that we want to force the GC to report any
331 dead Malloc Pointers to us.
333 Note: this should only be called using _ccall_GC_ which saves all
334 registers in the usual place (ie the global save area) before the
335 call and restores them afterwards.
337 ToDo: put in a runtime check that _ccall_GC_ is in action. */
340 StgPerformGarbageCollection()
342 # if ! defined(__STG_GCC_REGS__)
343 SaveAllStgRegs(); /* unregisterised case */
346 RealPerformGC(0,0,0,rtsTrue);
348 # if ! defined(__STG_GCC_REGS__)
349 RestoreAllStgRegs(); /* unregisterised case */
358 /* Jim's spark pools are very similar to our processors, except that
359 he uses a hard-wired constant. This would be a mistake for us,
360 since we won't always need this many pools.
363 PruneSparks(STG_NO_ARGS)
365 sparkq spark, prev, next;
366 I_ proc, pool, prunedSparks;
368 for(proc=0; proc<max_proc; ++proc) {
371 for (pool = 0; pool < SPARK_POOLS; pool++) {
374 for(spark = PendingSparksHd[proc][pool];
377 next = SPARK_NEXT(spark);
379 /* HACK! The first clause should actually never happen HWL */
381 if ( (SPARK_NODE(spark) == NULL) ||
382 (SPARK_NODE(spark) == Nil_closure) ) {
383 # if defined(GRAN_CHECK) && defined(GRAN)
385 fprintf(main_statsfile,"PruneSparks: Warning: spark @ 0x%lx points to NULL or Nil_closure\n", spark);
388 QP_Event0(threadId++, SPARK_NODE(spark));
391 DumpRawGranEvent(CURRENT_PROC,SP_PRUNED,(W_) spark);
396 else if (SHOULD_SPARK(SPARK_NODE(spark))) {
399 PendingSparksHd[proc][pool] = spark;
401 SPARK_NEXT(prev) = spark;
402 SPARK_PREV(spark) = prev;
406 QP_Event0(threadId++, SPARK_NODE(spark));
409 DumpRawGranEvent(CURRENT_PROC,SP_PRUNED,(W_) spark);
414 } /* forall spark ... */
416 PendingSparksHd[proc][pool] = NULL;
418 SPARK_NEXT(prev) = NULL;
419 PendingSparksTl[proc][pool] = prev;
421 fprintf(main_statsfile,"Pruning and disposing %lu excess sparks (> %lu) on proc %ld in PruneSparks\n",
422 prunedSparks,(W_) MAX_SPARKS,proc);
423 } /* forall pool ... */
424 } /* forall proc ... */
430 PruneSparks(STG_NO_ARGS)
437 for (pool = 0; pool < SPARK_POOLS; pool++) {
438 new = PendingSparksBase[pool];
439 for (old = PendingSparksHd[pool]; old < PendingSparksTl[pool]; old++) {
440 if (SHOULD_SPARK(*old)) {
445 QP_Event0(threadId++, *old);
448 DumpSparkGranEvent(SP_PRUNED, threadId++);
452 PendingSparksHd[pool] = PendingSparksBase[pool];
453 PendingSparksTl[pool] = new;
461 This is the real GC wrapper for the threaded world. No context
462 switching or other nonsense... just set up StorageMgrInfo and perform
463 a garbage collection.
468 ReallyPerformThreadGC(reqsize, do_full_collection)
470 rtsBool do_full_collection;
476 I_ num_ptr_roots = 0; /* we bump this counter as we store roots; de-bump it
477 as we re-store them. */
480 /* Discard the saved stack and TSO space */
482 for(stack = AvailableStack; stack != Nil_closure; stack = next) {
483 next = STKO_LINK(stack);
484 FREEZE_MUT_HDR(stack, ImMutArrayOfPtrs_info);
485 MUTUPLE_CLOSURE_SIZE(stack) = MUTUPLE_VHS;
488 for(tso = AvailableTSO; tso != Nil_closure; tso = next) {
489 next = TSO_LINK(tso);
490 FREEZE_MUT_HDR(tso, ImMutArrayOfPtrs_info);
491 MUTUPLE_CLOSURE_SIZE(tso) = MUTUPLE_VHS;
494 AvailableStack = AvailableTSO = Nil_closure;
499 for(proc = 0; proc < max_proc; ++proc) {
502 for(i = 0; i < SPARK_POOLS; i++) {
503 if (PendingSparksHd[proc][i] != NULL)
504 StorageMgrInfo.roots[num_ptr_roots++] = PendingSparksHd[proc][i];
505 if ( PendingSparksTl[proc][i] != NULL)
506 StorageMgrInfo.roots[num_ptr_roots++] = PendingSparksTl[proc][i];
510 # if defined(GRAN_CHECK) && defined(GRAN)
512 fprintf(main_statsfile,"Saving RunnableThreadsHd %d (proc: %d) -- 0x%lx\n",
513 num_ptr_roots,proc,RunnableThreadsHd[proc]);
516 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsHd[proc];
518 # if defined(GRAN_CHECK) && defined(GRAN)
520 fprintf(main_statsfile,"Saving RunnableThreadsTl %d (proc: %d) -- 0x%lx\n",
521 num_ptr_roots,proc,RunnableThreadsTl[proc]);
523 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsTl[proc];
524 } /* forall proc ... */
526 num_ptr_roots = SaveSparkRoots(num_ptr_roots);
527 num_ptr_roots = SaveEventRoots(num_ptr_roots);
531 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsHd;
532 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsTl;
533 StorageMgrInfo.roots[num_ptr_roots++] = WaitingThreadsHd;
534 StorageMgrInfo.roots[num_ptr_roots++] = WaitingThreadsTl;
538 # if defined(GRAN_CHECK) && defined(GRAN)
540 fprintf(main_statsfile,"Saving CurrentTSO %d -- 0x%lx\n",
541 num_ptr_roots,CurrentTSO);
544 StorageMgrInfo.roots[num_ptr_roots++] = CurrentTSO;
547 StorageMgrInfo.roots[num_ptr_roots++] = PendingFetches;
550 StorageMgrInfo.rootno = num_ptr_roots;
554 if (collectHeap(reqsize, &StorageMgrInfo, do_full_collection) != GC_SUCCESS) {
556 OutOfHeapHook(reqsize * sizeof(W_), SM_word_heap_size * sizeof(W_)); /*msg*/
558 # if defined(DO_REDN_COUNTING)
559 if (showRednCountStats) {
560 PrintRednCountInfo();
566 StorageMgrInfo.rootno = 0; /* reset */
568 /* root restoring ------------------------------- */
569 /* must do all the restoring exactly backwards to the storing! */
571 # if defined(GRAN_CHECK) && defined(GRAN)
573 fprintf(main_statsfile,"Restoring CurrentTSO %d -- new: 0x%lx\n",
574 num_ptr_roots-1,StorageMgrInfo.roots[num_ptr_roots-1]);
578 PendingFetches = StorageMgrInfo.roots[--num_ptr_roots];
580 CurrentTSO = StorageMgrInfo.roots[--num_ptr_roots];
581 CurrentRegTable = TSO_INTERNAL_PTR(CurrentTSO);
585 WaitingThreadsTl = StorageMgrInfo.roots[--num_ptr_roots];
586 WaitingThreadsHd = StorageMgrInfo.roots[--num_ptr_roots];
588 RunnableThreadsTl = StorageMgrInfo.roots[--num_ptr_roots];
589 RunnableThreadsHd = StorageMgrInfo.roots[--num_ptr_roots];
593 num_ptr_roots = RestoreEventRoots(num_ptr_roots);
594 num_ptr_roots = RestoreSparkRoots(num_ptr_roots);
596 /* NB: PROC is unsigned datatype i.e. (PROC)-1 == (PROC)255 */
598 for(proc = max_proc - 1; (proc >= 0) && (proc < max_proc) ; --proc) {
600 # if defined(GRAN_CHECK) && defined(GRAN)
602 fprintf(main_statsfile,"Restoring RunnableThreadsTl %d (proc: %d) -- new: 0x%lx\n",
603 num_ptr_roots-1,proc,StorageMgrInfo.roots[num_ptr_roots-1]);
606 RunnableThreadsTl[proc] = StorageMgrInfo.roots[--num_ptr_roots];
608 # if defined(GRAN_CHECK) && defined(GRAN)
610 fprintf(main_statsfile,"Restoring RunnableThreadsHd %d (proc: %d) -- new: 0x%lx\n",
611 num_ptr_roots,proc,StorageMgrInfo.roots[num_ptr_roots]);
614 RunnableThreadsHd[proc] = StorageMgrInfo.roots[--num_ptr_roots];
617 for(i = SPARK_POOLS - 1; i >= 0; --i) {
618 if (PendingSparksTl[proc][i] != NULL)
619 PendingSparksTl[proc][i] = StorageMgrInfo.roots[--num_ptr_roots];
620 if (PendingSparksHd[proc][i] != NULL)
621 PendingSparksHd[proc][i] = StorageMgrInfo.roots[--num_ptr_roots];
628 /* Semantics of GC ensures that a block of `reqsize' is now available */
631 unblockUserSignals();
634 #endif /* CONCURRENT */
638 This routine rattles down the B stack, black-holing any
639 pending updates to avoid space leaks from them.
642 #if !defined(CONCURRENT) && defined(SM_DO_BH_UPDATE)
646 BlackHoleUpdateStack(STG_NO_ARGS)
653 PtrToUpdateFrame = MAIN_SuB;
655 /* ToDo: There may be an optimisation here which stops at the first
656 BHed closure on the stack as all below must have been BHed */
658 while (SUBTRACT_B_STK(PtrToUpdateFrame, stackInfo.botB) > 0) {
660 UPD_BH(GRAB_UPDATEE(PtrToUpdateFrame), BH_UPD_info);
662 /* Move PtrToUpdateFrame down B Stack */
663 PtrToUpdateFrame = GRAB_SuB(PtrToUpdateFrame);
666 #endif /* CONCURRENT && SM_DO_BH_UPDATE */
671 #if defined(CONCURRENT) && !defined(GRAN)
673 PerformReschedule(liveness, always_reenter_node)
675 W_ always_reenter_node;