pyarena.c 5.8 KB
Newer Older
1 2 3
#include "Python.h"
#include "pyarena.h"

4
/* A simple arena block structure.
5 6 7 8

   Measurements with standard library modules suggest the average
   allocation is about 20 bytes and that most compiles use a single
   block.
Jeremy Hylton's avatar
Jeremy Hylton committed
9 10 11

   TODO(jhylton): Think about a realloc API, maybe just for the last
   allocation?
12
*/
13

14
#define DEFAULT_BLOCK_SIZE 8192
15 16 17 18
#define ALIGNMENT		8
#define ALIGNMENT_MASK		(ALIGNMENT - 1)
#define ROUNDUP(x)		(((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK)

19
typedef struct _block {
20 21 22 23
	/* Total number of bytes owned by this block available to pass out.
	 * Read-only after initialization.  The first such byte starts at
	 * ab_mem.
	 */
Jeremy Hylton's avatar
Jeremy Hylton committed
24
	size_t ab_size;
25 26 27 28

	/* Total number of bytes already passed out.  The next byte available
	 * to pass out starts at ab_mem + ab_offset.
	 */
Jeremy Hylton's avatar
Jeremy Hylton committed
29
	size_t ab_offset;
30 31 32 33 34

	/* An arena maintains a singly-linked, NULL-terminated list of
	 * all blocks owned by the arena.  These are linked via the
	 * ab_next member.
	 */
Jeremy Hylton's avatar
Jeremy Hylton committed
35
	struct _block *ab_next;
36 37 38 39

	/* Pointer to the first allocatable byte owned by this block.  Read-
	 * only after initialization.
	 */
Jeremy Hylton's avatar
Jeremy Hylton committed
40
	void *ab_mem;
41
} block;
42

43 44 45 46
/* The arena manages two kinds of memory, blocks of raw memory
   and a list of PyObject* pointers.  PyObjects are decrefed
   when the arena is freed.
*/
47

48
struct _arena {
Jeremy Hylton's avatar
Jeremy Hylton committed
49 50 51 52
        /* Pointer to the first block allocated for the arena, never NULL.
           It is used only to find the first block when the arena is
           being freed.
         */
Jeremy Hylton's avatar
Jeremy Hylton committed
53
	block *a_head;
Jeremy Hylton's avatar
Jeremy Hylton committed
54 55 56 57 58 59

        /* Pointer to the block currently used for allocation.  It's
           ab_next field should be NULL.  If it is not-null after a
           call to block_alloc(), it means a new block has been allocated
           and a_cur should be reset to point it.
         */
Jeremy Hylton's avatar
Jeremy Hylton committed
60
	block *a_cur;
Jeremy Hylton's avatar
Jeremy Hylton committed
61 62 63 64 65

        /* A Python list object containing references to all the PyObject
           pointers associated with this area.  They will be DECREFed
           when the arena is freed.
        */
66
        PyObject *a_objects;
Jeremy Hylton's avatar
Jeremy Hylton committed
67

68 69 70 71 72 73 74 75
#if defined(Py_DEBUG)
        /* Debug output */
        size_t total_allocs;
        size_t total_size;
        size_t total_blocks;
        size_t total_block_size;
        size_t total_big_blocks;
#endif
76 77
};

78 79
static block *
block_new(size_t size)
80
{
81
	/* Allocate header and block as one unit.
Jeremy Hylton's avatar
Jeremy Hylton committed
82 83 84 85 86 87 88
	   ab_mem points just past header. */
	block *b = (block *)malloc(sizeof(block) + size);
	if (!b)
		return NULL;
	b->ab_size = size;
	b->ab_mem = (void *)(b + 1);
	b->ab_next = NULL;
89 90
	b->ab_offset = ROUNDUP((Py_uintptr_t)(b->ab_mem)) - 
	  (Py_uintptr_t)(b->ab_mem);
Jeremy Hylton's avatar
Jeremy Hylton committed
91
	return b;
92 93 94 95
}

static void
block_free(block *b) {
Jeremy Hylton's avatar
Jeremy Hylton committed
96 97 98 99 100
	while (b) {
		block *next = b->ab_next;
		free(b);
		b = next;
	}
101 102
}

103 104 105
static void *
block_alloc(block *b, size_t size)
{
Jeremy Hylton's avatar
Jeremy Hylton committed
106 107
	void *p;
	assert(b);
108
	size = ROUNDUP(size);
Jeremy Hylton's avatar
Jeremy Hylton committed
109 110 111 112 113
	if (b->ab_offset + size > b->ab_size) {
		/* If we need to allocate more memory than will fit in
		   the default block, allocate a one-off block that is
		   exactly the right size. */
		/* TODO(jhylton): Think about space waste at end of block */
114
		block *newbl = block_new(
Jeremy Hylton's avatar
Jeremy Hylton committed
115 116
				size < DEFAULT_BLOCK_SIZE ?
				DEFAULT_BLOCK_SIZE : size);
117
		if (!newbl)
Jeremy Hylton's avatar
Jeremy Hylton committed
118 119
			return NULL;
		assert(!b->ab_next);
120 121
		b->ab_next = newbl;
		b = newbl;
Jeremy Hylton's avatar
Jeremy Hylton committed
122 123 124 125 126 127
	}

	assert(b->ab_offset + size <= b->ab_size);
	p = (void *)(((char *)b->ab_mem) + b->ab_offset);
	b->ab_offset += size;
	return p;
128
}
129 130 131 132

PyArena *
PyArena_New()
{
Jeremy Hylton's avatar
Jeremy Hylton committed
133 134
	PyArena* arena = (PyArena *)malloc(sizeof(PyArena));
	if (!arena)
135
		return (PyArena*)PyErr_NoMemory();
Jeremy Hylton's avatar
Jeremy Hylton committed
136 137 138

	arena->a_head = block_new(DEFAULT_BLOCK_SIZE);
	arena->a_cur = arena->a_head;
139 140
        if (!arena->a_head) {
                free((void *)arena);
141
                return (PyArena*)PyErr_NoMemory();
142
        }
143
        arena->a_objects = PyList_New(0);
144 145 146
        if (!arena->a_objects) {
                block_free(arena->a_head);
                free((void *)arena);
147
                return (PyArena*)PyErr_NoMemory();
148
        }
149 150 151 152 153 154 155
#if defined(Py_DEBUG)
        arena->total_allocs = 0;
        arena->total_size = 0;
        arena->total_blocks = 1;
        arena->total_block_size = DEFAULT_BLOCK_SIZE;
        arena->total_big_blocks = 0;
#endif
Jeremy Hylton's avatar
Jeremy Hylton committed
156
	return arena;
157 158 159 160 161
}

void
PyArena_Free(PyArena *arena)
{
Jeremy Hylton's avatar
Jeremy Hylton committed
162
        int r;
Jeremy Hylton's avatar
Jeremy Hylton committed
163
	assert(arena);
164 165
#if defined(Py_DEBUG)
        /*
166
        fprintf(stderr,
167 168 169 170 171 172
                "alloc=%d size=%d blocks=%d block_size=%d big=%d objects=%d\n",
                arena->total_allocs, arena->total_size, arena->total_blocks,
                arena->total_block_size, arena->total_big_blocks,
                PyList_Size(arena->a_objects));
        */
#endif
Jeremy Hylton's avatar
Jeremy Hylton committed
173
	block_free(arena->a_head);
174 175
	/* This property normally holds, except when the code being compiled
	   is sys.getobjects(0), in which case there will be two references.
176
        assert(arena->a_objects->ob_refcnt == 1);
177
	*/
Jeremy Hylton's avatar
Jeremy Hylton committed
178 179 180 181 182 183 184

        /* Clear all the elements from the list.  This is necessary
           to guarantee that they will be DECREFed. */
        r = PyList_SetSlice(arena->a_objects,
                            0, PyList_GET_SIZE(arena->a_objects), NULL);
        assert(r == 0);
        assert(PyList_GET_SIZE(arena->a_objects) == 0);
185
        Py_DECREF(arena->a_objects);
Jeremy Hylton's avatar
Jeremy Hylton committed
186
	free(arena);
187 188 189
}

void *
190
PyArena_Malloc(PyArena *arena, size_t size)
191
{
Jeremy Hylton's avatar
Jeremy Hylton committed
192 193
	void *p = block_alloc(arena->a_cur, size);
	if (!p)
194
		return PyErr_NoMemory();
195 196 197 198
#if defined(Py_DEBUG)
        arena->total_allocs++;
        arena->total_size += size;
#endif
Jeremy Hylton's avatar
Jeremy Hylton committed
199 200 201
	/* Reset cur if we allocated a new block. */
	if (arena->a_cur->ab_next) {
		arena->a_cur = arena->a_cur->ab_next;
202 203 204 205
#if defined(Py_DEBUG)
                arena->total_blocks++;
                arena->total_block_size += arena->a_cur->ab_size;
                if (arena->a_cur->ab_size > DEFAULT_BLOCK_SIZE)
206
                        ++arena->total_big_blocks;
207
#endif
Jeremy Hylton's avatar
Jeremy Hylton committed
208 209
	}
	return p;
210 211 212
}

int
213
PyArena_AddPyObject(PyArena *arena, PyObject *obj)
214
{
Jeremy Hylton's avatar
Jeremy Hylton committed
215 216 217 218 219
        int r = PyList_Append(arena->a_objects, obj);
        if (r >= 0) {
                Py_DECREF(obj);
        }
        return r;
220
}