1 #include "Python.h"
2 #include "pyarena.h"
3 
4 /* A simple arena block structure.
5 
6    Measurements with standard library modules suggest the average
7    allocation is about 20 bytes and that most compiles use a single
8    block.
9 
10    TODO(jhylton): Think about a realloc API, maybe just for the last
11    allocation?
12 */
13 
14 #define DEFAULT_BLOCK_SIZE 8192
15 #define ALIGNMENT               8
16 #define ALIGNMENT_MASK          (ALIGNMENT - 1)
17 #define ROUNDUP(x)              (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK)
18 
19 typedef struct _block {
20     /* Total number of bytes owned by this block available to pass out.
21      * Read-only after initialization.  The first such byte starts at
22      * ab_mem.
23      */
24     size_t ab_size;
25 
26     /* Total number of bytes already passed out.  The next byte available
27      * to pass out starts at ab_mem + ab_offset.
28      */
29     size_t ab_offset;
30 
31     /* An arena maintains a singly-linked, NULL-terminated list of
32      * all blocks owned by the arena.  These are linked via the
33      * ab_next member.
34      */
35     struct _block *ab_next;
36 
37     /* Pointer to the first allocatable byte owned by this block.  Read-
38      * only after initialization.
39      */
40     void *ab_mem;
41 } block;
42 
43 /* The arena manages two kinds of memory, blocks of raw memory
44    and a list of PyObject* pointers.  PyObjects are decrefed
45    when the arena is freed.
46 */
47 
48 struct _arena {
49     /* Pointer to the first block allocated for the arena, never NULL.
50        It is used only to find the first block when the arena is
51        being freed.
52      */
53     block *a_head;
54 
55     /* Pointer to the block currently used for allocation.  It's
56        ab_next field should be NULL.  If it is not-null after a
57        call to block_alloc(), it means a new block has been allocated
58        and a_cur should be reset to point it.
59      */
60     block *a_cur;
61 
62     /* A Python list object containing references to all the PyObject
63        pointers associated with this area.  They will be DECREFed
64        when the arena is freed.
65     */
66     PyObject *a_objects;
67 
68 #if defined(Py_DEBUG)
69     /* Debug output */
70     size_t total_allocs;
71     size_t total_size;
72     size_t total_blocks;
73     size_t total_block_size;
74     size_t total_big_blocks;
75 #endif
76 };
77 
78 static block *
block_new(size_t size)79 block_new(size_t size)
80 {
81     /* Allocate header and block as one unit.
82        ab_mem points just past header. */
83     block *b = (block *)malloc(sizeof(block) + size);
84     if (!b)
85         return NULL;
86     b->ab_size = size;
87     b->ab_mem = (void *)(b + 1);
88     b->ab_next = NULL;
89     b->ab_offset = ROUNDUP((Py_uintptr_t)(b->ab_mem)) -
90       (Py_uintptr_t)(b->ab_mem);
91     return b;
92 }
93 
94 static void
block_free(block * b)95 block_free(block *b) {
96     while (b) {
97         block *next = b->ab_next;
98         free(b);
99         b = next;
100     }
101 }
102 
103 static void *
block_alloc(block * b,size_t size)104 block_alloc(block *b, size_t size)
105 {
106     void *p;
107     assert(b);
108     size = ROUNDUP(size);
109     if (b->ab_offset + size > b->ab_size) {
110         /* If we need to allocate more memory than will fit in
111            the default block, allocate a one-off block that is
112            exactly the right size. */
113         /* TODO(jhylton): Think about space waste at end of block */
114         block *newbl = block_new(
115                         size < DEFAULT_BLOCK_SIZE ?
116                         DEFAULT_BLOCK_SIZE : size);
117         if (!newbl)
118             return NULL;
119         assert(!b->ab_next);
120         b->ab_next = newbl;
121         b = newbl;
122     }
123 
124     assert(b->ab_offset + size <= b->ab_size);
125     p = (void *)(((char *)b->ab_mem) + b->ab_offset);
126     b->ab_offset += size;
127     return p;
128 }
129 
130 PyArena *
PyArena_New()131 PyArena_New()
132 {
133     PyArena* arena = (PyArena *)malloc(sizeof(PyArena));
134     if (!arena)
135         return (PyArena*)PyErr_NoMemory();
136 
137     arena->a_head = block_new(DEFAULT_BLOCK_SIZE);
138     arena->a_cur = arena->a_head;
139     if (!arena->a_head) {
140         free((void *)arena);
141         return (PyArena*)PyErr_NoMemory();
142     }
143     arena->a_objects = PyList_New(0);
144     if (!arena->a_objects) {
145         block_free(arena->a_head);
146         free((void *)arena);
147         return (PyArena*)PyErr_NoMemory();
148     }
149 #if defined(Py_DEBUG)
150     arena->total_allocs = 0;
151     arena->total_size = 0;
152     arena->total_blocks = 1;
153     arena->total_block_size = DEFAULT_BLOCK_SIZE;
154     arena->total_big_blocks = 0;
155 #endif
156     return arena;
157 }
158 
159 void
PyArena_Free(PyArena * arena)160 PyArena_Free(PyArena *arena)
161 {
162     assert(arena);
163 #if defined(Py_DEBUG)
164     /*
165     fprintf(stderr,
166         "alloc=%d size=%d blocks=%d block_size=%d big=%d objects=%d\n",
167         arena->total_allocs, arena->total_size, arena->total_blocks,
168         arena->total_block_size, arena->total_big_blocks,
169         PyList_Size(arena->a_objects));
170     */
171 #endif
172     block_free(arena->a_head);
173     /* This property normally holds, except when the code being compiled
174        is sys.getobjects(0), in which case there will be two references.
175     assert(arena->a_objects->ob_refcnt == 1);
176     */
177 
178     Py_DECREF(arena->a_objects);
179     free(arena);
180 }
181 
182 void *
PyArena_Malloc(PyArena * arena,size_t size)183 PyArena_Malloc(PyArena *arena, size_t size)
184 {
185     void *p = block_alloc(arena->a_cur, size);
186     if (!p)
187         return PyErr_NoMemory();
188 #if defined(Py_DEBUG)
189     arena->total_allocs++;
190     arena->total_size += size;
191 #endif
192     /* Reset cur if we allocated a new block. */
193     if (arena->a_cur->ab_next) {
194         arena->a_cur = arena->a_cur->ab_next;
195 #if defined(Py_DEBUG)
196         arena->total_blocks++;
197         arena->total_block_size += arena->a_cur->ab_size;
198         if (arena->a_cur->ab_size > DEFAULT_BLOCK_SIZE)
199             ++arena->total_big_blocks;
200 #endif
201     }
202     return p;
203 }
204 
205 int
PyArena_AddPyObject(PyArena * arena,PyObject * obj)206 PyArena_AddPyObject(PyArena *arena, PyObject *obj)
207 {
208     int r = PyList_Append(arena->a_objects, obj);
209     if (r >= 0) {
210         Py_DECREF(obj);
211     }
212     return r;
213 }
214