Coverage Report

Created: 2022-07-08 09:39

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