summaryrefslogtreecommitdiff
path: root/rts/Arena.c
blob: 8129475332f9a50285bd7a50a3e1f44321227dc3 (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
/* -----------------------------------------------------------------------------
   (c) The University of Glasgow 2001

   Arena allocation.  Arenas provide fast memory allocation at the
   expense of fine-grained recycling of storage: memory may be
   only be returned to the system by freeing the entire arena, it
   isn't possible to return individual objects within an arena.

   Do not assume that sequentially allocated objects will be adjacent
   in memory.

   Quirks: this allocator makes use of the RTS block allocator.  If
   the current block doesn't have enough room for the requested
   object, then a new block is allocated.  This means that allocating
   large objects will tend to result in wasted space at the end of
   each block.  In the worst case, half of the allocated space is
   wasted.  This allocator is therefore best suited to situations in
   which most allocations are small.
   -------------------------------------------------------------------------- */

#include "rts/PosixSource.h"
#include "Rts.h"

#include "RtsUtils.h"
#include "Arena.h"

// Each arena struct is allocated using malloc().
struct _Arena {
    bdescr *current;
    StgWord *free;              // ptr to next free byte in current block
    StgWord *lim;               // limit (== last free byte + 1)
};

// We like to keep track of how many blocks we've allocated for
// Storage.c:memInventory().
static long arena_blocks = 0;

// Begin a new arena
Arena *
newArena( void )
{
    Arena *arena;

    arena = stgMallocBytes(sizeof(Arena), "newArena");
    arena->current = allocBlock_lock();
    arena->current->link = NULL;
    arena->free = arena->current->start;
    arena->lim  = arena->current->start + BLOCK_SIZE_W;
    arena_blocks++;

    return arena;
}

// The minimum alignment of an allocated block.
#define MIN_ALIGN 8

/* 'n' is assumed to be a power of 2 */
#define ROUNDUP(x,n)  (((x)+((n)-1))&(~((n)-1)))
#define B_TO_W(x)     ((x) / sizeof(W_))

// Allocate some memory in an arena
void  *
arenaAlloc( Arena *arena, size_t size )
{
    void *p;
    uint32_t size_w;
    uint32_t req_blocks;
    bdescr *bd;

    // round up to nearest alignment chunk.
    size = ROUNDUP(size,MIN_ALIGN);

    // size of allocated block in words.
    size_w = B_TO_W(size);

    if ( arena->free + size_w < arena->lim ) {
        // enough room in the current block...
        p = arena->free;
        arena->free += size_w;
        return p;
    } else {
        // allocate a fresh block...
        req_blocks =  (W_)BLOCK_ROUND_UP(size) / BLOCK_SIZE;
        bd = allocGroup_lock(req_blocks);
        arena_blocks += bd->blocks;

        bd->gen_no  = 0;
        bd->gen     = NULL;
        bd->dest_no = 0;
        bd->flags   = 0;
        bd->free    = bd->start;
        bd->link    = arena->current;
        arena->current = bd;
        arena->free = bd->free + size_w;
        arena->lim = bd->free + bd->blocks * BLOCK_SIZE_W;
        return bd->start;
    }
}

// Free an entire arena
void
arenaFree( Arena *arena )
{
    bdescr *bd, *next;

    for (bd = arena->current; bd != NULL; bd = next) {
        next = bd->link;
        arena_blocks -= bd->blocks;
        ASSERT(arena_blocks >= 0);
        freeGroup_lock(bd);
    }
    stgFree(arena);
}

unsigned long
arenaBlocks( void )
{
    return arena_blocks;
}

#if defined(DEBUG)
void checkPtrInArena( StgPtr p, Arena *arena )
{
    // We don't update free pointers of arena blocks, so we have to check cached
    // free pointer for the first block.
    if (p >= arena->current->start && p < arena->free) {
        return;
    }

    // Rest of the blocks should be full (except there may be a little bit of
    // slop at the end). Again, free pointers are not updated so we can't use
    // those.
    for (bdescr *bd = arena->current->link; bd; bd = bd->link) {
        if (p >= bd->start && p < bd->start + (bd->blocks*BLOCK_SIZE_W)) {
            return;
        }
    }

    barf("Location %p is not in arena %p", (void*)p, (void*)arena);
}
#endif