summaryrefslogtreecommitdiff
path: root/rts/sm/GCUtils.c
blob: 636f23dc23959192d13baa18ccda2a6a16c72aca (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* -----------------------------------------------------------------------------
 *
 * (c) The GHC Team 1998-2006
 *
 * Generational garbage collector: utilities
 *
 * Documentation on the architecture of the Garbage Collector can be
 * found in the online commentary:
 * 
 *   http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Storage/GC
 *
 * ---------------------------------------------------------------------------*/

#include "Rts.h"
#include "RtsFlags.h"
#include "Storage.h"
#include "GC.h"
#include "GCUtils.h"
#include "Printer.h"
#include "Trace.h"

#ifdef THREADED_RTS
SpinLock gc_alloc_block_sync;
#endif

bdescr *
allocBlock_sync(void)
{
    bdescr *bd;
    ACQUIRE_SPIN_LOCK(&gc_alloc_block_sync);
    bd = allocBlock();
    RELEASE_SPIN_LOCK(&gc_alloc_block_sync);
    return bd;
}

void
freeChain_sync(bdescr *bd)
{
    ACQUIRE_SPIN_LOCK(&gc_alloc_block_sync);
    freeChain(bd);
    RELEASE_SPIN_LOCK(&gc_alloc_block_sync);
}

/* -----------------------------------------------------------------------------
   Workspace utilities
   -------------------------------------------------------------------------- */

bdescr *
grab_todo_block (step_workspace *ws)
{
    bdescr *bd;
    step *stp;

    stp = ws->stp;
    bd = NULL;

    if (ws->buffer_todo_bd)
    {
	bd = ws->buffer_todo_bd;
	ASSERT(bd->link == NULL);
	ws->buffer_todo_bd = NULL;
	return bd;
    }

    ACQUIRE_SPIN_LOCK(&stp->sync_todo);
    if (stp->todos) {
	bd = stp->todos;
	stp->todos = bd->link;
        stp->n_todos--;
	bd->link = NULL;
    }	
    RELEASE_SPIN_LOCK(&stp->sync_todo);
    return bd;
}

static void
push_todo_block (bdescr *bd, step *stp)
{
    ASSERT(bd->link == NULL);
    ACQUIRE_SPIN_LOCK(&stp->sync_todo);
    bd->link = stp->todos;
    stp->todos = bd;
    stp->n_todos++;
    trace(TRACE_gc, "step %d, n_todos: %d", stp->abs_no, stp->n_todos);
    RELEASE_SPIN_LOCK(&stp->sync_todo);
}

void
push_scan_block (bdescr *bd, step_workspace *ws)
{
    ASSERT(bd != NULL);
    ASSERT(bd->link == NULL);

    // update stats: this is a block that has been copied & scavenged
    gct->copied += bd->free - bd->start;

    // put the scan block on the ws->scavd_list.
    bd->link = ws->scavd_list;
    ws->scavd_list = bd;
    ws->n_scavd_blocks ++;

    IF_DEBUG(sanity, 
	     ASSERT(countBlocks(ws->scavd_list) == ws->n_scavd_blocks));
}

StgPtr
gc_alloc_todo_block (step_workspace *ws)
{
    bdescr *bd;

    if (ws->todo_bd != NULL) {
        ws->todo_bd->free = ws->todo_free;
    }

    // If we already have a todo block, it must be full, so we push it
    // out: first to the buffer_todo_bd, then to the step.  BUT, don't
    // push out the block out if it is already the scan block.
    if (ws->todo_bd != NULL && ws->scan_bd != ws->todo_bd) {
	ASSERT(ws->todo_bd->link == NULL);
        if (ws->buffer_todo_bd == NULL) {
            // If the global todo list is empty, push this block
            // out immediately rather than caching it in
            // buffer_todo_bd, because there might be other threads
            // waiting for work.
            if (ws->stp->todos == NULL) {
                push_todo_block(ws->todo_bd, ws->stp);
            } else {
                ws->buffer_todo_bd = ws->todo_bd;
            }
        } else {            
	    ASSERT(ws->buffer_todo_bd->link == NULL);
	    push_todo_block(ws->buffer_todo_bd, ws->stp);
            ws->buffer_todo_bd = ws->todo_bd;
        }
	ws->todo_bd = NULL;
    }	    

    bd = allocBlock_sync();

    bd->gen_no = ws->stp->gen_no;
    bd->step = ws->stp;
    bd->link = NULL;

    // blocks in to-space in generations up to and including N
    // get the BF_EVACUATED flag.
    if (ws->stp->gen_no <= N) {
	bd->flags = BF_EVACUATED;
    } else {
	bd->flags = 0;
    }
	
    ws->todo_bd = bd;
    ws->todo_free = bd->start;
    ws->todo_lim  = bd->start + BLOCK_SIZE_W;

    return ws->todo_free;
}

/* -----------------------------------------------------------------------------
 * Debugging
 * -------------------------------------------------------------------------- */

#if DEBUG
void
printMutableList(generation *gen)
{
    bdescr *bd;
    StgPtr p;

    debugBelch("mutable list %p: ", gen->mut_list);

    for (bd = gen->mut_list; bd != NULL; bd = bd->link) {
	for (p = bd->start; p < bd->free; p++) {
	    debugBelch("%p (%s), ", (void *)*p, info_type((StgClosure *)*p));
	}
    }
    debugBelch("\n");
}
#endif /* DEBUG */