summaryrefslogtreecommitdiff
path: root/ghc/rts/Weak.c
blob: 462ceccb4db516dadb4dc3d5cc33a1eaad476844 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* -----------------------------------------------------------------------------
 * $Id: Weak.c,v 1.31 2003/10/24 09:00:59 simonmar Exp $
 *
 * (c) The GHC Team, 1998-1999
 *
 * Weak pointers / finalizers
 *
 * ---------------------------------------------------------------------------*/

#include "PosixSource.h"
#define COMPILING_RTS_MAIN
#include "Rts.h"
#include "SchedAPI.h"
#include "RtsFlags.h"
#include "Weak.h"
#include "Storage.h"
#include "Schedule.h"
#include "Prelude.h"
#include "RtsAPI.h"

StgWeak *weak_ptr_list;

/*
 * finalizeWeakPointersNow() is called just before the system is shut
 * down.  It runs the finalizer for each weak pointer still in the
 * system.
 *
 * Careful here - rts_evalIO might cause a garbage collection, which
 * might change weak_ptr_list.  Must re-load weak_ptr_list each time
 * around the loop.
 */

void
finalizeWeakPointersNow(void)
{
  StgWeak *w;
  
  rts_lock();
  while ((w = weak_ptr_list)) {
    weak_ptr_list = w->link;
    if (w->header.info != &stg_DEAD_WEAK_info) {
	w->header.info = &stg_DEAD_WEAK_info;
	IF_DEBUG(weak,fprintf(stderr,"Finalising weak pointer at %p -> %p\n", w, w->key));
	if (w->finalizer != &stg_NO_FINALIZER_closure) {
	    rts_evalLazyIO(w->finalizer,NULL);
	    rts_unlock();
	    rts_lock();
	}
    }
  }
  rts_unlock();
} 

/*
 * scheduleFinalizers() is called on the list of weak pointers found
 * to be dead after a garbage collection.  It overwrites each object
 * with DEAD_WEAK, and creates a new thread to run the pending finalizers.
 *
 * This function is called just after GC.  The weak pointers on the
 * argument list are those whose keys were found to be not reachable,
 * however the value and finalizer fields have by now been marked live.
 * The weak pointer object itself may not be alive - i.e. we may be
 * looking at either an object in from-space or one in to-space.  It
 * doesn't really matter either way.
 *
 * Pre-condition: sched_mutex _not_ held.
 */

void
scheduleFinalizers(StgWeak *list)
{
    StgWeak *w;
    StgTSO *t;
    StgMutArrPtrs *arr;
    nat n;

    // count number of finalizers, and kill all the weak pointers first...
    n = 0;
    for (w = list; w; w = w->link) { 

	// Better not be a DEAD_WEAK at this stage; the garbage
	// collector removes DEAD_WEAKs from the weak pointer list.
	ASSERT(w->header.info != &stg_DEAD_WEAK_info);

	if (w->finalizer != &stg_NO_FINALIZER_closure) {
	    n++;
	}

#ifdef PROFILING
        // A weak pointer is inherently used, so we do not need to call
        // LDV_recordDead().
	//
        // Furthermore, when PROFILING is turned on, dead weak
        // pointers are exactly as large as weak pointers, so there is
        // no need to fill the slop, either.  See stg_DEAD_WEAK_info
        // in StgMiscClosures.hc.
#endif
	SET_HDR(w, &stg_DEAD_WEAK_info, w->header.prof.ccs);
    }
	
    // No finalizers to run?
    if (n == 0) return;

    IF_DEBUG(weak,fprintf(stderr,"weak: batching %d finalizers\n", n));

    arr = (StgMutArrPtrs *)allocate(sizeofW(StgMutArrPtrs) + n);
    TICK_ALLOC_PRIM(sizeofW(StgMutArrPtrs), n, 0);
    SET_HDR(arr, &stg_MUT_ARR_PTRS_FROZEN_info, CCS_SYSTEM);
    arr->mut_link = NULL;
    arr->ptrs = n;

    n = 0;
    for (w = list; w; w = w->link) {
	if (w->finalizer != &stg_NO_FINALIZER_closure) {
	    arr->payload[n] = w->finalizer;
	    n++;
	}
    }

    t = createIOThread(RtsFlags.GcFlags.initialStkSize, 
		       rts_apply(
			   rts_apply(
			       (StgClosure *)runFinalizerBatch_closure,
			       rts_mkInt(n)), 
			   (StgClosure *)arr)
	);
    scheduleThread(t);
}