8000 Move to delayed decref · python/cpython@ad58b75 · GitHub
[go: up one dir, main page]

Skip to content

Commit ad58b75

Browse files
committed
Move to delayed decref
1 parent 0fe4abc commit ad58b75

File tree

8 files changed

+30
-92
lines changed

8 files changed

+30
-92
lines changed

Include/internal/pycore_gc.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ static inline PyObject* _Py_FROM_GC(PyGC_Head *gc) {
5050
# define _PyGC_BITS_UNREACHABLE (4)
5151
# define _PyGC_BITS_FROZEN (8)
5252
# define _PyGC_BITS_SHARED (16)
53-
# define _PyGC_BITS_SHARED_INLINE (32)
5453
# define _PyGC_BITS_DEFERRED (64) // Use deferred reference counting
5554
#endif
5655

@@ -119,19 +118,6 @@ static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
119118
}
120119
#define _PyObject_GC_SET_SHARED(op) _PyObject_GC_SET_SHARED(_Py_CAST(PyObject*, op))
121120

122-
/* True if the memory of the object is shared between multiple
123-
* threads and needs special purpose when freeing due to
124-
* the possibility of in-flight lock-free reads occurring.
125-
* Objects with this bit that are GC objects will automatically
126-
* delay-freed by PyObject_GC_Del. */
127-
static inline int _PyObject_GC_IS_SHARED_INLINE(PyObject *op) {
128-
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_SHARED_INLINE);
129-
}
130-
#define _PyObject_GC_IS_SHARED_INLINE(op) \
131-
_PyObject_GC_IS_SHARED_INLINE(_Py_CAST(PyObject*, op))
132-
133-
extern void _PyObject_GC_SET_SHARED_INLINE(PyObject *op);
134-
135121
#endif
136122

137123
/* Bit flags for _gc_prev */

Include/internal/pycore_pymem.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,14 @@ extern int _PyMem_DebugEnabled(void);
120120
extern void _PyMem_FreeDelayed(void *ptr);
121121

122122
// Enqueue an object to be freed possibly after some delay
123-
extern void _PyObject_FreeDelayed(void *ptr);
123+
#ifdef Py_GIL_DISABLED
124+
extern void _PyObject_XDecRefDelayed(PyObject *obj);
125+
#else
126+
static inline void _PyObject_XDecRefDelayed(PyObject *obj)
127+
{
128+
Py_XDECREF(obj);
129+
}
130+
#endif
124131

125132
// Periodically process delayed free requests.
126133
extern void _PyMem_ProcessDelayed(PyThreadState *tstate);

Include/refcount.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,6 @@ PyAPI_FUNC(void) _Py_DecRefSharedDebug(PyObject *, const char *, int);
278278
// count fields.
279279
PyAPI_FUNC(void) _Py_MergeZeroLocalRefcount(PyObject *);
280280

281-
// Queue an object to be deferred at the next quiescent state
282-
PyAPI_FUNC(void) _PyObject_FreeDeferred(PyObject *o);
283-
284281
#endif
285282

286283
#if defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG))

Objects/dictobject.c

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,6 @@ ASSERT_DICT_LOCKED(PyObject *op)
165165

166166
#define IS_DICT_SHARED(mp) _PyObject_GC_IS_SHARED(mp)
167167
#define SET_DICT_SHARED(mp) _PyObject_GC_SET_SHARED(mp)
168-
#define IS_DICT_SHARED_INLINE(mp) _PyObject_GC_IS_SHARED_INLINE(mp)
169-
#define SET_DICT_SHARED_INLINE(mp) _PyObject_GC_SET_SHARED_INLINE(mp)
170168
#define LOAD_INDEX(keys, size, idx) _Py_atomic_load_int##size##_relaxed(&((const int##size##_t*)keys->dk_indices)[idx]);
171169
#define STORE_INDEX(keys, size, idx, value) _Py_atomic_store_int##size##_relaxed(&((int##size##_t*)keys->dk_indices)[idx], (int##size##_t)value);
172170
#define ASSERT_OWNED_OR_SHARED(mp) \
@@ -247,8 +245,6 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys)
247245
#define UNLOCK_KEYS_IF_SPLIT(keys, kind)
248246
#define IS_DICT_SHARED(mp) (false)
249247
#define SET_DICT_SHARED(mp)
250-
#define IS_DICT_SHARED_INLINE(mp) (false)
251-
#define SET_DICT_SHARED_INLINE(mp)
252248
#define LOAD_INDEX(keys, size, idx) ((const int##size##_t*)(keys->dk_indices))[idx]
253249
#define STORE_INDEX(keys, size, idx, value) ((int##size##_t*)(keys->dk_indices))[idx] = (int##size##_t)value
254250

@@ -3202,7 +3198,7 @@ dict_dealloc(PyObject *self)
32023198
assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS);
32033199
dictkeys_decref(interp, keys, false);
32043200
}
3205-
if (Py_IS_TYPE(mp, &PyDict_Type) && !IS_DICT_SHARED_INLINE(mp)) {
3201+
if (Py_IS_TYPE(mp, &PyDict_Type)) {
32063202
_Py_FREELIST_FREE(dicts, mp, Py_TYPE(mp)->tp_free);
32073203
}
32083204
else {
@@ -7167,8 +7163,6 @@ try_set_dict_inline_only_or_other_dict(PyObject *obj, PyObject *new_dict, PyDict
71677163
return replaced;
71687164
}
71697165

7170-
#endif
7171-
71727166
// Replaces a dictionary that is probably the dictionary which has been
71737167
// materialized and points at the inline values. We could have raced
71747168
// and replaced it with another dictionary though.
@@ -7187,18 +7181,20 @@ replace_dict_probably_inline_materialized(PyObject *obj, PyDictObject *inline_di
71877181
if (err != 0) {
71887182
return err;
71897183
}
7190-
SET_DICT_SHARED_INLINE((PyObject *)inline_dict);
71917184
// We incref'd the inline dict and the object owns a ref.
71927185
// Clear the object's reference, we'll clear the local
71937186
// reference after releasing the lock.
7194-
Py_CLEAR(*replaced_dict);
7187+
_PyObject_XDecRefDelayed((PyObject *)*replaced_dict);
7188+
*replaced_dict = NULL;
71957189
}
71967190

71977191
FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
71987192
(PyDictObject *)Py_XNewRef(new_dict));
71997193
return err;
72007194
}
72017195

7196+
#endif
7197+
72027198
int
72037199
_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
72047200
{
@@ -7231,12 +7227,9 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
72317227
}
72327228

72337229
if (prev_dict != NULL) {
7234-
Py_BEGIN_CRITICAL_SECTION(prev_dict);
7235-
SET_DICT_SHARED_INLINE((PyObject *)prev_dict);
7236-
Py_END_CRITICAL_SECTION();
72377230
// Readers from the old dictionary use a borrowed reference. We need
7238-
// to set the dict to be freed via QSBR which requires locking it.
7239-
Py_DECREF(prev_dict);
7231+
// to set the decref the dict at the next safe point.
7232+
_PyObject_XDecRefDelayed((PyObject *)prev_dict);
72407233
}
72417234
return 0;
72427235
#else
@@ -7264,14 +7257,7 @@ _PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
72647257
(PyDictObject *)Py_XNewRef(new_dict));
72657258

72667259
Py_END_CRITICAL_SECTION();
7267-
#ifdef Py_GIL_DISABLED
7268-
if (dict != NULL) {
7269-
Py_BEGIN_CRITICAL_SECTION(dict);
7270-
SET_DICT_SHARED_INLINE((PyObject *)dict);
7271-
Py_END_CRITICAL_SECTION();
7272-
}
7273-
#endif
7274-
Py_XDECREF(dict);
7260+
_PyObject_XDecRefDelayed((PyObject *)dict);
72757261
}
72767262
assert(_PyObject_InlineValuesConsistencyCheck(obj));
72777263
return err;

Objects/object.c

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -319,27 +319,6 @@ is_dead(PyObject *o)
319319
}
320320
# endif
321321

322-
void
323-
_PyObject_FreeDeferred(PyObject *o)
324-
{
325-
// It's possible that before we've hit the quiescent-state that it
326-
// has been incref'd and then decref'd back to zero again. We re-use
327-
// the finalized bit to track if we've already queued the delay freed
328-
// for this object as we no longer need to GC track it if it's being
329-
// freed by QSBR as it's reclaimed by the ref count.
330-
if (!_PyGC_FINALIZED(o)) {
331-
Py_BEGIN_CRITICAL_SECTION(o);
332-
if (!_PyGC_FINALIZED(o)) {
333-
if (_PyObject_GC_IS_TRACKED(o)) {
334-
PyObject_GC_UnTrack(o);
335-
}
336-
_PyGC_SET_FINALIZED(o);
337-
_PyObject_FreeDelayed(o);
338-
}
339-
Py_END_CRITICAL_SECTION();
340-
}
341-
}
342-
343322
void
344323
_Py_DecRefSharedDebug(PyObject *o, const char *filename, int lineno)
345324
{
@@ -381,12 +360,8 @@ _Py_DecRefSharedDebug(PyObject *o, const char *filename, int lineno)
381360
_Py_brc_queue_object(o);
382361
}
383362
else if (new_shared == _Py_REF_MERGED) {
384-
if (!_PyObject_GC_IS_SHARED_INLINE(o)) {
385-
// refcount is zero AND merged
386-
_Py_Dealloc(o);
387-
} else {
388-
_PyObject_FreeDeferred(o);
389-
}
363+
// refcount is zero AND merged
364+
_Py_Dealloc(o);
390365
}
391366
}
392367

@@ -404,12 +379,8 @@ _Py_MergeZeroLocalRefcount(PyObject *op)
404379
Py_ssize_t shared = _Py_atomic_load_ssize_acquire(&op->ob_ref_shared);
405380
if (shared == 0) {
406381
// Fast-path: shared refcount is zero (including flags)
407-
if (!_PyObject_GC_IS_SHARED_INLINE(op)) {
408-
// refcount is zero AND merged
409-
_Py_Dealloc(op);
410-
} else {
411-
_PyObject_FreeDeferred(op);
412-
}
382+
// refcount is zero AND merged
383+
_Py_Dealloc(op);
413384
return;
414385
}
415386

@@ -428,12 +399,8 @@ _Py_MergeZeroLocalRefcount(PyObject *op)
428399
if (new_shared == _Py_REF_MERGED) {
429400
// i.e., the shared refcount is zero (only the flags are set) so we
430401
// deallocate the object.
431-
if (!_PyObject_GC_IS_SHARED_INLINE(op)) {
432-
// refcount is zero AND merged
433-
_Py_Dealloc(op);
434-
} else {
435-
_PyObject_FreeDeferred(op);
436-
}
402+
// refcount is zero AND merged
403+
_Py_Dealloc(op);
437404
}
438405
}
439406

Objects/obmalloc.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ static void
10961096
free_work_item(uintptr_t ptr)
10971097
{
10981098
if (ptr & 0x01) {
1099-
_Py_Dealloc((PyObject*)(char *)(ptr - 1));
1099+
Py_DECREF((PyObject*)(char *)(ptr - 1));
11001100
}
11011101
else {
11021102
PyMem_Free((void *)ptr);
@@ -1166,12 +1166,16 @@ _PyMem_FreeDelayed(void *ptr)
11661166
free_delayed((uintptr_t)ptr);
11671167
}
11681168

1169+
#ifdef Py_GIL_DISABLED
11691170
void
1170-
_PyObject_FreeDelayed(void *ptr)
1171+
_PyObject_XDecRefDelayed(PyObject *ptr)
11711172
{
11721173
assert(!((uintptr_t)ptr & 0x01));
1173-
free_delayed(((uintptr_t)ptr)|0x01);
1174+
if (ptr != NULL) {
1175+
free_delayed(((uintptr_t)ptr)|0x01);
1176+
}
11741177
}
1178+
#endif
11751179

11761180
static struct _mem_work_chunk *
11771181
work_queue_first(struct llist_node *head)

Python/brc.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,7 @@ merge_queued_objects(_PyObjectStack *to_merge)
107107
// Subtract one when merging because the queue had a reference.
108108
Py_ssize_t refcount = _Py_ExplicitMergeRefcount(ob, -1);
109109
if (refcount == 0) {
110-
if (!_PyObject_GC_IS_SHARED_INLINE(ob)) {
111-
_Py_Dealloc(ob);
112-
} else {
113-
_PyObject_FreeDeferred(ob);
114-
}
110+
_Py_Dealloc(ob);
115111
}
116112
}
117113
}

Python/gc_free_threading.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,6 @@ delete_garbage(struct collection_state *state)
924924
// as cyclic junk as the world is stopped and no one has a
925925
// borrowed reference to it and we'll avoid pushing it to be
926926
// delay freed.
927-
_PyObject_CLEAR_GC_BITS(op, _PyGC_BITS_SHARED_INLINE);
928927
inquiry clear = Py_TYPE(op)->tp_clear;
929928
if (clear != NULL) {
930929
(void) clear(op);
@@ -1993,8 +1992,4 @@ _PyGC_ClearAllFreeLists(PyInterpreterState *interp)
19931992
HEAD_UNLOCK(&_PyRuntime);
19941993
}
19951994

1996-
void _PyObject_GC_SET_SHARED_INLINE(PyObject *op) {
1997-
_PyObject_SET_GC_BITS(op, _PyGC_BITS_SHARED_INLINE);
1998-
}
1999-
20001995
#endif // Py_GIL_DISABLED

0 commit comments

Comments
 (0)
0