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
  • Objects
  • Python
  • 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