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 PrintTickyInfo(STG_NO_ARGS);
42 #if defined(GRAN_CHECK) && defined(GRAN)
46 /* the real work is done by this function --- see wrappers at end */
49 RealPerformGC(liveness, reqsize, always_reenter_node, do_full_collection)
52 W_ always_reenter_node;
53 rtsBool do_full_collection;
55 I_ num_ptr_roots = 0; /* we bump this counter as we
56 store roots; de-bump it
57 as we re-store them. */
58 #if defined(PROFILING)
62 /* stop the profiling timer --------------------- */
63 #if defined(PROFILING)
64 /* STOP_TIME_PROFILER; */
69 SAVE_Liveness = liveness;
72 fprintf(stderr,"RealGC:liveness=0x%lx,reqsize=0x%lx,reenter=%lx,do_full=%d,context_switch=%ld\n",
73 liveness, reqsize,always_reenter_node,do_full_collection,context_switch);
77 Even on a uniprocessor, we may have to reenter node after a
78 context switch. Though it can't turn into a FetchMe, its shape
79 may have changed (e.g. from a thunk to a data object).
81 if (always_reenter_node) {
82 /* Avoid infinite loops at the same heap check */
83 if (SAVE_Hp <= SAVE_HpLim && TSO_SWITCH(CurrentTSO) == TSO_PC2(CurrentTSO)) {
84 TSO_SWITCH(CurrentTSO) = NULL;
87 /* Set up to re-enter Node, so as to be sure it's really there. */
88 ASSERT(liveness & LIVENESS_R1);
89 TSO_SWITCH(CurrentTSO) = TSO_PC2(CurrentTSO);
90 TSO_PC2(CurrentTSO) = EnterNodeCode;
95 if (context_switch && !do_full_collection
96 # if defined(PROFILING)
100 /* We're in a GC callWrapper, so the thread state is safe */
101 TSO_ARG1(CurrentTSO) = reqsize;
102 TSO_PC1(CurrentTSO) = CheckHeapCode;
104 if (RTSflags.ParFlags.granSimStats) {
105 TSO_EXECTIME(CurrentTSO) += CURRENT_TIME - TSO_BLOCKEDAT(CurrentTSO);
109 ReSchedule(9 /*i.e. error; was SAME_THREAD*/);
115 /* Don't use SET_CCC, because we don't want to bump the sub_scc_count */
116 # if defined(PROFILING)
120 CCC = (CostCentre)STATIC_CC_REF(CC_GC);
124 ReallyPerformThreadGC(reqsize, do_full_collection);
126 #else /* !CONCURRENT */
128 # if defined(PROFILING)
129 /* Don't use SET_CCC, because we don't want to bump the sub_scc_count */
131 CCC = (CostCentre)STATIC_CC_REF(CC_GC);
135 /* root saving ---------------------------------- */
137 # define __ENROOT_PTR_REG(cond,n) /* n == 1 <=> R1 */ \
139 StorageMgrInfo.roots[num_ptr_roots] = CAT2(MAIN_R,n).p; \
143 __ENROOT_PTR_REG(IS_LIVE_R1(liveness),1);
144 __ENROOT_PTR_REG(IS_LIVE_R2(liveness),2);
145 __ENROOT_PTR_REG(IS_LIVE_R3(liveness),3);
146 __ENROOT_PTR_REG(IS_LIVE_R4(liveness),4);
147 __ENROOT_PTR_REG(IS_LIVE_R5(liveness),5);
148 __ENROOT_PTR_REG(IS_LIVE_R6(liveness),6);
149 __ENROOT_PTR_REG(IS_LIVE_R7(liveness),7);
150 __ENROOT_PTR_REG(IS_LIVE_R8(liveness),8);
153 * Before we garbage collect we may have to squeeze update frames and/or
154 * black hole the update stack
156 if (! RTSflags.GcFlags.squeezeUpdFrames) {
157 BlackHoleUpdateStack();
159 } else { /* Squeeze and/or black hole update frames */
162 displacement = SqueezeUpdateFrames(stackInfo.botB + BREL(1), MAIN_SpB, MAIN_SuB);
164 MAIN_SuB += BREL(displacement);
165 MAIN_SpB += BREL(displacement);
166 /* fprintf(stderr, "B size %d, squeezed out %d\n", MAIN_SpB - stackInfo.botB,
170 ASSERT(num_ptr_roots <= SM_MAXROOTS);
171 StorageMgrInfo.rootno = num_ptr_roots;
174 /* Move (SAVE_)Hp back to where it was */
175 /* (heap is known to grow upwards) */
176 /* we *do* have to do this, so reported stats will be right! */
178 /* the main business ---------------------------- */
185 /* Restore hpLim to its "correct" setting */
186 StorageMgrInfo.hplim += StorageMgrInfo.hardHpOverflowSize;
188 GC_result = collectHeap(reqsize, &StorageMgrInfo, do_full_collection);
190 if ( GC_result == GC_HARD_LIMIT_EXCEEDED ) {
191 OutOfHeapHook(reqsize * sizeof(W_)); /*msg*/
195 } else if ( GC_result == GC_SOFT_LIMIT_EXCEEDED ) {
196 /* Allow ourselves to use emergency space */
197 /* Set hplim so that we'll GC when we hit the soft limit */
198 StorageMgrInfo.hplim -= StorageMgrInfo.hardHpOverflowSize;
199 raiseError( softHeapOverflowHandler );
201 } else if ( GC_result == GC_SUCCESS ) {
202 /* Set hplim so that we'll GC when we hit the soft limit */
203 StorageMgrInfo.hplim -= StorageMgrInfo.hardHpOverflowSize;
205 } else { /* This should not happen */
206 fprintf(stderr, "Panic: garbage collector returned %d please report it as a bug to glasgow-haskell-bugs@dcs.gla.ac.uk\n", GC_result );
208 # if defined(TICKY_TICKY)
209 if (RTSflags.TickyFlags.showTickyStats) PrintTickyInfo();
215 StorageMgrInfo.rootno = 0; /* reset */
218 /* Semantics of GC ensures that a block of
219 `reqsize' is now available (and allocated) [NB: sequential only] */
221 /* root restoring ------------------------------- */
222 /* must do all the restoring exactly backwards to the storing! */
224 /* now the general regs, in *backwards* order */
226 # define __DEROOT_PTR_REG(cond,n) /* n == 1 <=> R1 */ \
229 CAT2(MAIN_R,n).p = StorageMgrInfo.roots[num_ptr_roots]; \
232 __DEROOT_PTR_REG(IS_LIVE_R8(liveness),8);
233 __DEROOT_PTR_REG(IS_LIVE_R7(liveness),7);
234 __DEROOT_PTR_REG(IS_LIVE_R6(liveness),6);
235 __DEROOT_PTR_REG(IS_LIVE_R5(liveness),5);
236 __DEROOT_PTR_REG(IS_LIVE_R4(liveness),4);
237 __DEROOT_PTR_REG(IS_LIVE_R3(liveness),3);
238 __DEROOT_PTR_REG(IS_LIVE_R2(liveness),2);
239 __DEROOT_PTR_REG(IS_LIVE_R1(liveness),1);
241 ASSERT(num_ptr_roots == 0); /* we have put it all back */
243 unblockUserSignals();
245 #endif /* !CONCURRENT */
247 #if defined(PROFILING)
250 RESTART_TIME_PROFILER;
255 This is a wrapper used for all standard, non-threaded, non-parallel GC
258 #ifdef HEAP_CHK_HYGIENE
259 I_ doHygieneCheck = 0;
266 W_ liveness = HEAP_OVERFLOW_LIVENESS(args);
267 W_ reqsize = HEAP_OVERFLOW_REQSIZE(args);
268 W_ always_reenter_node = HEAP_OVERFLOW_REENTER(args);
270 #ifdef HEAP_CHK_HYGIENE
271 if (doHygieneCheck) {
276 RealPerformGC(liveness, reqsize, always_reenter_node, rtsFalse);
279 #if defined(CONCURRENT) && defined(GRAN)
280 /* This is directly called from the macro GRAN_RESCHEDULE out of the */
281 /* threaded world. -- HWL */
284 PerformReschedule(liveness, always_reenter_node)
286 W_ always_reenter_node;
289 I_ need_to_reschedule;
291 /* Reset the global NeedToReSchedule --
292 this is used only to communicate the fact that we should schedule
293 a new thread rather than the existing one following a fetch.
295 need_to_reschedule = NeedToReSchedule;
296 NeedToReSchedule = rtsFalse;
298 SAVE_Liveness = liveness;
300 if (always_reenter_node) {
301 /* Avoid infinite loops at the same context switch */
302 if ((TSO_SWITCH(CurrentTSO) == TSO_PC2(CurrentTSO)) &&
303 !need_to_reschedule) {
304 TSO_SWITCH(CurrentTSO) = NULL;
308 /* Set up to re-enter Node, so as to be sure it's really there. */
309 ASSERT(liveness & LIVENESS_R1);
310 TSO_SWITCH(CurrentTSO) = TSO_PC2(CurrentTSO);
311 TSO_PC2(CurrentTSO) = (void *) EnterNodeCode;
314 /* We're in a GC callWrapper, so the thread state is safe */
315 TSO_ARG1(CurrentTSO) = 0;
316 TSO_PC1(CurrentTSO) = EnterNodeCode;
317 ReSchedule( (need_to_reschedule && !DoReScheduleOnFetch) ?
318 CHANGE_THREAD : SAME_THREAD );
323 /* this is a wrapper used when we want to do a full GC.
325 One reason might be that we're about to enter a time-critical piece
326 of code and want to reduce the risk of a GC during the run. The
327 motivating reason is that we want to force the GC to report any
328 dead Malloc Pointers to us.
330 Note: this should only be called using _ccall_GC_ which saves all
331 registers in the usual place (ie the global save area) before the
332 call and restores them afterwards.
334 ToDo: put in a runtime check that _ccall_GC_ is in action. */
337 StgPerformGarbageCollection()
339 # if ! defined(__STG_GCC_REGS__)
340 SaveAllStgRegs(); /* unregisterised case */
343 RealPerformGC(0,0,0,rtsTrue);
345 # if ! defined(__STG_GCC_REGS__)
346 RestoreAllStgRegs(); /* unregisterised case */
355 /* Jim's spark pools are very similar to our processors, except that
356 he uses a hard-wired constant. This would be a mistake for us,
357 since we won't always need this many pools.
360 PruneSparks(STG_NO_ARGS)
362 sparkq spark, prev, next;
363 I_ proc, pool, prunedSparks;
365 for(proc=0; proc<max_proc; ++proc) {
368 for (pool = 0; pool < SPARK_POOLS; pool++) {
371 for(spark = PendingSparksHd[proc][pool];
374 next = SPARK_NEXT(spark);
376 /* HACK! The first clause should actually never happen HWL */
378 if ( (SPARK_NODE(spark) == NULL) ||
379 (SPARK_NODE(spark) == Nil_closure) ) {
380 # if defined(GRAN_CHECK) && defined(GRAN)
382 fprintf(RTSflags.GcFlags.statsFile,"PruneSparks: Warning: spark @ 0x%lx points to NULL or Nil_closure\n", spark);
385 QP_Event0(threadId++, SPARK_NODE(spark));
388 DumpRawGranEvent(CURRENT_PROC,SP_PRUNED,(W_) spark);
393 else if (SHOULD_SPARK(SPARK_NODE(spark))) {
396 PendingSparksHd[proc][pool] = spark;
398 SPARK_NEXT(prev) = spark;
399 SPARK_PREV(spark) = prev;
403 QP_Event0(threadId++, SPARK_NODE(spark));
406 DumpRawGranEvent(CURRENT_PROC,SP_PRUNED,(W_) spark);
411 } /* forall spark ... */
413 PendingSparksHd[proc][pool] = NULL;
415 SPARK_NEXT(prev) = NULL;
416 PendingSparksTl[proc][pool] = prev;
418 fprintf(RTSflags.GcFlags.statsFile,"Pruning and disposing %lu excess sparks (> %lu) on proc %ld in PruneSparks\n",
419 prunedSparks,(W_) MAX_SPARKS,proc);
420 } /* forall pool ... */
421 } /* forall proc ... */
427 PruneSparks(STG_NO_ARGS)
434 for (pool = 0; pool < SPARK_POOLS; pool++) {
435 new = PendingSparksBase[pool];
436 for (old = PendingSparksHd[pool]; old < PendingSparksTl[pool]; old++) {
437 if (SHOULD_SPARK(*old)) {
442 QP_Event0(threadId++, *old);
445 DumpSparkGranEvent(SP_PRUNED, threadId++);
449 PendingSparksHd[pool] = PendingSparksBase[pool];
450 PendingSparksTl[pool] = new;
458 This is the real GC wrapper for the threaded world. No context
459 switching or other nonsense... just set up StorageMgrInfo and perform
460 a garbage collection.
465 ReallyPerformThreadGC(reqsize, do_full_collection)
467 rtsBool do_full_collection;
473 I_ num_ptr_roots = 0; /* we bump this counter as we store roots; de-bump it
474 as we re-store them. */
477 /* Discard the saved stack and TSO space.
478 What's going on here: TSOs and StkOs are on the mutables
479 list (mutable things in the old generation). Here, we change
480 them to immutable, so that the scavenger (which chks all
481 mutable objects) can detect their immutability and remove
482 them from the list. Setting to MUTUPLE_VHS as the size is
483 essentially saying "No pointers in here" (i.e., empty).
485 Without this change of status, these
486 objects might not really die, probably with some horrible
487 disastrous consequence that we don't want to think about.
491 for(stack = AvailableStack; stack != Nil_closure; stack = next) {
492 next = STKO_LINK(stack);
493 FREEZE_MUT_HDR(stack, ImMutArrayOfPtrs_info);
494 MUTUPLE_CLOSURE_SIZE(stack) = MUTUPLE_VHS;
497 for(tso = AvailableTSO; tso != Nil_closure; tso = next) {
498 next = TSO_LINK(tso);
499 FREEZE_MUT_HDR(tso, ImMutArrayOfPtrs_info);
500 MUTUPLE_CLOSURE_SIZE(tso) = MUTUPLE_VHS;
503 AvailableStack = AvailableTSO = Nil_closure;
508 for(proc = 0; proc < max_proc; ++proc) {
511 for(i = 0; i < SPARK_POOLS; i++) {
512 if (PendingSparksHd[proc][i] != NULL)
513 StorageMgrInfo.roots[num_ptr_roots++] = PendingSparksHd[proc][i];
514 if ( PendingSparksTl[proc][i] != NULL)
515 StorageMgrInfo.roots[num_ptr_roots++] = PendingSparksTl[proc][i];
519 # if defined(GRAN_CHECK) && defined(GRAN)
521 fprintf(RTSflags.GcFlags.statsFile,"Saving RunnableThreadsHd %d (proc: %d) -- 0x%lx\n",
522 num_ptr_roots,proc,RunnableThreadsHd[proc]);
525 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsHd[proc];
527 # if defined(GRAN_CHECK) && defined(GRAN)
529 fprintf(RTSflags.GcFlags.statsFile,"Saving RunnableThreadsTl %d (proc: %d) -- 0x%lx\n",
530 num_ptr_roots,proc,RunnableThreadsTl[proc]);
532 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsTl[proc];
533 } /* forall proc ... */
535 num_ptr_roots = SaveSparkRoots(num_ptr_roots);
536 num_ptr_roots = SaveEventRoots(num_ptr_roots);
540 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsHd;
541 StorageMgrInfo.roots[num_ptr_roots++] = RunnableThreadsTl;
542 StorageMgrInfo.roots[num_ptr_roots++] = WaitingThreadsHd;
543 StorageMgrInfo.roots[num_ptr_roots++] = WaitingThreadsTl;
547 # if defined(GRAN_CHECK) && defined(GRAN)
549 fprintf(RTSflags.GcFlags.statsFile,"Saving CurrentTSO %d -- 0x%lx\n",
550 num_ptr_roots,CurrentTSO);
553 StorageMgrInfo.roots[num_ptr_roots++] = CurrentTSO;
556 StorageMgrInfo.roots[num_ptr_roots++] = PendingFetches;
559 StorageMgrInfo.rootno = num_ptr_roots;
563 if (collectHeap(reqsize, &StorageMgrInfo, do_full_collection) != GC_SUCCESS) {
565 OutOfHeapHook(reqsize * sizeof(W_)); /*msg*/
567 # if defined(TICKY_TICKY)
568 if (RTSflags.TickyFlags.showTickyStats) PrintTickyInfo();
573 StorageMgrInfo.rootno = 0; /* reset */
575 /* root restoring ------------------------------- */
576 /* must do all the restoring exactly backwards to the storing! */
578 # if defined(GRAN_CHECK) && defined(GRAN)
580 fprintf(RTSflags.GcFlags.statsFile,"Restoring CurrentTSO %d -- new: 0x%lx\n",
581 num_ptr_roots-1,StorageMgrInfo.roots[num_ptr_roots-1]);
585 PendingFetches = StorageMgrInfo.roots[--num_ptr_roots];
587 CurrentTSO = StorageMgrInfo.roots[--num_ptr_roots];
588 CurrentRegTable = TSO_INTERNAL_PTR(CurrentTSO);
592 WaitingThreadsTl = StorageMgrInfo.roots[--num_ptr_roots];
593 WaitingThreadsHd = StorageMgrInfo.roots[--num_ptr_roots];
595 RunnableThreadsTl = StorageMgrInfo.roots[--num_ptr_roots];
596 RunnableThreadsHd = StorageMgrInfo.roots[--num_ptr_roots];
600 num_ptr_roots = RestoreEventRoots(num_ptr_roots);
601 num_ptr_roots = RestoreSparkRoots(num_ptr_roots);
603 /* NB: PROC is unsigned datatype i.e. (PROC)-1 == (PROC)255 */
605 for(proc = max_proc - 1; (proc >= 0) && (proc < max_proc) ; --proc) {
607 # if defined(GRAN_CHECK) && defined(GRAN)
609 fprintf(RTSflags.GcFlags.statsFile,"Restoring RunnableThreadsTl %d (proc: %d) -- new: 0x%lx\n",
610 num_ptr_roots-1,proc,StorageMgrInfo.roots[num_ptr_roots-1]);
613 RunnableThreadsTl[proc] = StorageMgrInfo.roots[--num_ptr_roots];
615 # if defined(GRAN_CHECK) && defined(GRAN)
617 fprintf(RTSflags.GcFlags.statsFile,"Restoring RunnableThreadsHd %d (proc: %d) -- new: 0x%lx\n",
618 num_ptr_roots,proc,StorageMgrInfo.roots[num_ptr_roots]);
621 RunnableThreadsHd[proc] = StorageMgrInfo.roots[--num_ptr_roots];
624 for(i = SPARK_POOLS - 1; i >= 0; --i) {
625 if (PendingSparksTl[proc][i] != NULL)
626 PendingSparksTl[proc][i] = StorageMgrInfo.roots[--num_ptr_roots];
627 if (PendingSparksHd[proc][i] != NULL)
628 PendingSparksHd[proc][i] = StorageMgrInfo.roots[--num_ptr_roots];
635 /* Semantics of GC ensures that a block of `reqsize' is now available */
638 unblockUserSignals();
641 #endif /* CONCURRENT */
645 This routine rattles down the B stack, black-holing any
646 pending updates to avoid space leaks from them.
649 #if !defined(CONCURRENT)
653 BlackHoleUpdateStack(STG_NO_ARGS)
657 if (! RTSflags.GcFlags.lazyBlackHoling)
660 PtrToUpdateFrame = MAIN_SuB;
662 /* ToDo: There may be an optimisation here which stops at the first
663 BHed closure on the stack as all below must have been BHed */
665 while (SUBTRACT_B_STK(PtrToUpdateFrame, stackInfo.botB) > 0) {
667 UPD_BH(GRAB_UPDATEE(PtrToUpdateFrame), BH_UPD_info);
669 /* Move PtrToUpdateFrame down B Stack */
670 PtrToUpdateFrame = GRAB_SuB(PtrToUpdateFrame);
673 #endif /* CONCURRENT */
678 #if defined(CONCURRENT) && !defined(GRAN)
680 PerformReschedule(W_ liveness, W_ always_reenter_node)