• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Stack of Python objects
2 
3 #include "Python.h"
4 #include "pycore_freelist.h"
5 #include "pycore_pystate.h"
6 #include "pycore_object_stack.h"
7 
8 extern _PyObjectStackChunk *_PyObjectStackChunk_New(void);
9 extern void _PyObjectStackChunk_Free(_PyObjectStackChunk *);
10 
11 static struct _Py_object_stack_freelist *
get_object_stack_freelist(void)12 get_object_stack_freelist(void)
13 {
14     struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
15     return &freelists->object_stacks;
16 }
17 
18 _PyObjectStackChunk *
_PyObjectStackChunk_New(void)19 _PyObjectStackChunk_New(void)
20 {
21     _PyObjectStackChunk *buf;
22     struct _Py_object_stack_freelist *obj_stack_freelist = get_object_stack_freelist();
23     if (obj_stack_freelist->numfree > 0) {
24         buf = obj_stack_freelist->items;
25         obj_stack_freelist->items = buf->prev;
26         obj_stack_freelist->numfree--;
27     }
28     else {
29         // NOTE: we use PyMem_RawMalloc() here because this is used by the GC
30         // during mimalloc heap traversal. In that context, it is not safe to
31         // allocate mimalloc memory, such as via PyMem_Malloc().
32         buf = PyMem_RawMalloc(sizeof(_PyObjectStackChunk));
33         if (buf == NULL) {
34             return NULL;
35         }
36     }
37     buf->prev = NULL;
38     buf->n = 0;
39     return buf;
40 }
41 
42 void
_PyObjectStackChunk_Free(_PyObjectStackChunk * buf)43 _PyObjectStackChunk_Free(_PyObjectStackChunk *buf)
44 {
45     assert(buf->n == 0);
46     struct _Py_object_stack_freelist *obj_stack_freelist = get_object_stack_freelist();
47     if (obj_stack_freelist->numfree >= 0 &&
48         obj_stack_freelist->numfree < _PyObjectStackChunk_MAXFREELIST)
49     {
50         buf->prev = obj_stack_freelist->items;
51         obj_stack_freelist->items = buf;
52         obj_stack_freelist->numfree++;
53     }
54     else {
55         PyMem_RawFree(buf);
56     }
57 }
58 
59 void
_PyObjectStack_Clear(_PyObjectStack * queue)60 _PyObjectStack_Clear(_PyObjectStack *queue)
61 {
62     while (queue->head != NULL) {
63         _PyObjectStackChunk *buf = queue->head;
64         buf->n = 0;
65         queue->head = buf->prev;
66         _PyObjectStackChunk_Free(buf);
67     }
68 }
69 
70 void
_PyObjectStack_Merge(_PyObjectStack * dst,_PyObjectStack * src)71 _PyObjectStack_Merge(_PyObjectStack *dst, _PyObjectStack *src)
72 {
73     if (src->head == NULL) {
74         return;
75     }
76 
77     if (dst->head != NULL) {
78         // First, append dst to the bottom of src
79         _PyObjectStackChunk *last = src->head;
80         while (last->prev != NULL) {
81             last = last->prev;
82         }
83         last->prev = dst->head;
84     }
85 
86     // Now that src has all the chunks, set dst to src
87     dst->head = src->head;
88     src->head = NULL;
89 }
90 
91 void
_PyObjectStackChunk_ClearFreeList(struct _Py_object_freelists * freelists,int is_finalization)92 _PyObjectStackChunk_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization)
93 {
94     if (!is_finalization) {
95         // Ignore requests to clear the free list during GC. We use object
96         // stacks during GC, so emptying the free-list is counterproductive.
97         return;
98     }
99 
100     struct _Py_object_stack_freelist *freelist = &freelists->object_stacks;
101     while (freelist->numfree > 0) {
102         _PyObjectStackChunk *buf = freelist->items;
103         freelist->items = buf->prev;
104         freelist->numfree--;
105         PyMem_RawFree(buf);
106     }
107     freelist->numfree = -1;
108 }
109