Skip to content

Commit 1023064

Browse files
committed
Free objects with qsbr if shared
1 parent 88b5c66 commit 1023064

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed

Include/internal/pycore_pymem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ extern int _PyMem_DebugEnabled(void);
119119
// Enqueue a pointer to be freed possibly after some delay.
120120
extern void _PyMem_FreeDelayed(void *ptr);
121121

122+
// Enqueue an object to be freed possibly after some delay
123+
extern void _PyObject_FreeDelayed(void *ptr);
124+
125+
// Free all outstanding delayed freed objects. The world must be
126+
// stopped before calling this.
127+
extern void _PyMem_ProcessAllDelayed(PyInterpreterState *interp);
128+
122129
// Periodically process delayed free requests.
123130
extern void _PyMem_ProcessDelayed(PyThreadState *tstate);
124131

Objects/obmalloc.c

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ _PyMem_Strdup(const char *str)
957957

958958
// A pointer to be freed once the QSBR read sequence reaches qsbr_goal.
959959
struct _mem_work_item {
960-
void *ptr;
960+
void *ptr; // lowest bit tagged 1 for objects freed with PyObject_Free
961961
uint64_t qsbr_goal;
962962
};
963963

@@ -971,16 +971,26 @@ struct _mem_work_chunk {
971971
struct _mem_work_item array[WORK_ITEMS_PER_CHUNK];
972972
};
973973

974-
void
975-
_PyMem_FreeDelayed(void *ptr)
974+
void free_work_item(void *ptr)
975+
{
976+
if (((uintptr_t)ptr) & 0x01) {
977+
PyObject_Free(((char *)ptr) - 1);
978+
}
979+
else {
980+
PyMem_Free(ptr);
981+
}
982+
}
983+
984+
static void
985+
free_delayed(void *ptr)
976986
{
977987
#ifndef Py_GIL_DISABLED
978-
PyMem_Free(ptr);
988+
free_work_item(ptr);
979989
#else
980990
if (_PyRuntime.stoptheworld.world_stopped) {
981991
// Free immediately if the world is stopped, including during
982992
// interpreter shutdown.
983-
PyMem_Free(ptr);
993+
free_work_item(ptr);
984994
return;
985995
}
986996

@@ -1007,7 +1017,7 @@ _PyMem_FreeDelayed(void *ptr)
10071017
if (buf == NULL) {
10081018
// failed to allocate a buffer, free immediately
10091019
_PyEval_StopTheWorld(tstate->base.interp);
1010-
PyMem_Free(ptr);
1020+
free_work_item(ptr);
10111021
_PyEval_StartTheWorld(tstate->base.interp);
10121022
return;
10131023
}
@@ -1024,6 +1034,20 @@ _PyMem_FreeDelayed(void *ptr)
10241034
#endif
10251035
}
10261036

1037+
void
1038+
_PyMem_FreeDelayed(void *ptr)
1039+
{
1040+
assert(!((uintptr_t)ptr & 0x01));
1041+
free_delayed(ptr);
1042+
}
1043+
1044+
1045+
void _PyObject_FreeDelayed(void *ptr)
1046+
{
1047+
assert(!((uintptr_t)ptr & 0x01));
1048+
free_delayed((void *)(((uintptr_t)ptr)|0x01));
1049+
}
1050+
10271051
static struct _mem_work_chunk *
10281052
work_queue_first(struct llist_node *head)
10291053
{
@@ -1043,7 +1067,7 @@ process_queue(struct llist_node *head, struct _qsbr_thread_state *qsbr,
10431067
return;
10441068
}
10451069

1046-
PyMem_Free(item->ptr);
1070+
free_work_item(item->ptr);
10471071
buf->rd_idx++;
10481072
}
10491073

@@ -1119,6 +1143,23 @@ _PyMem_AbandonDelayed(PyThreadState *tstate)
11191143
assert(llist_empty(queue)); // the thread's queue is now empty
11201144
}
11211145

1146+
void
1147+
_PyMem_ProcessAllDelayed(PyInterpreterState *interp)
1148+
{
1149+
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
1150+
while (tstate != NULL) {
1151+
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
1152+
1153+
// Process thread-local work
1154+
process_queue(&tstate_impl->mem_free_queue, tstate_impl->qsbr, true);
1155+
1156+
tstate = PyThreadState_Next(tstate);
1157+
}
1158+
1159+
// Process shared interpreter work
1160+
process_interp_queue(&interp->mem_free_queue, ((_PyThreadStateImpl *)_PyThreadState_GET())->qsbr);
1161+
}
1162+
11221163
void
11231164
_PyMem_FiniDelayed(PyInterpreterState *interp)
11241165
{
@@ -1130,7 +1171,7 @@ _PyMem_FiniDelayed(PyInterpreterState *interp)
11301171
// Free the remaining items immediately. There should be no other
11311172
// threads accessing the memory at this point during shutdown.
11321173
struct _mem_work_item *item = &buf->array[buf->rd_idx];
1133-
PyMem_Free(item->ptr);
1174+
free_work_item(item->ptr);
11341175
buf->rd_idx++;
11351176
}
11361177

Python/gc_free_threading.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,11 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state)
10251025
merge_all_queued_objects(interp, state);
10261026
process_delayed_frees(interp);
10271027

1028+
// Free all outstanding objects. During finalization we could collect
1029+
// types before the underlying objects so its important that we not
1030+
// traverse any delay-freed objects.
1031+
_PyMem_ProcessAllDelayed(interp);
1032+
10281033
// Find unreachable objects
10291034
int err = deduce_unreachable_heap(interp, state);
10301035
if (err < 0) {
@@ -1704,8 +1709,13 @@ PyObject_GC_Del(void *op)
17041709
}
17051710

17061711
record_deallocation(_PyThreadState_GET());
1707-
1708-
PyObject_Free(((char *)op)-presize);
1712+
PyObject *self = (PyObject *)op;
1713+
if (_PyObject_GC_IS_SHARED(self)) {
1714+
_PyObject_FreeDelayed(((char *)op)-presize);
1715+
}
1716+
else {
1717+
PyObject_Free(((char *)op)-presize);
1718+
}
17091719
}
17101720

17111721
int

0 commit comments

Comments
 (0)