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