Skip to content

Commit a47da5f

Browse files
committed
Revert "[3.14] pythonGH-135106: Restrict trashcan to GC'ed objects (pythonGH-135682) (python#135876)"
This reverts commit 46a7981.
1 parent df79316 commit a47da5f

1 file changed

Lines changed: 52 additions & 24 deletions

File tree

Objects/object.c

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2923,28 +2923,57 @@ Py_ReprLeave(PyObject *obj)
29232923

29242924
/* Trashcan support. */
29252925

2926+
#ifndef Py_GIL_DISABLED
2927+
/* We need to store a pointer in the refcount field of
2928+
* an object. It is important that we never store 0 (NULL).
2929+
* It is also important to not make the object appear immortal,
2930+
* or it might be untracked by the cycle GC. */
2931+
static uintptr_t
2932+
pointer_to_safe_refcount(void *ptr)
2933+
{
2934+
uintptr_t full = (uintptr_t)ptr;
2935+
assert((full & 3) == 0);
2936+
#if SIZEOF_VOID_P > 4
2937+
uint32_t refcnt = (uint32_t)full;
2938+
if (refcnt >= (uint32_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
2939+
full = full - ((uintptr_t)_Py_IMMORTAL_MINIMUM_REFCNT) + 1;
2940+
}
2941+
return full + 2;
2942+
#else
2943+
// Make the top two bits 0, so it appears mortal.
2944+
return (full >> 2) + 1;
2945+
#endif
2946+
}
2947+
2948+
static void *
2949+
safe_refcount_to_pointer(uintptr_t refcnt)
2950+
{
2951+
#if SIZEOF_VOID_P > 4
2952+
if (refcnt & 1) {
2953+
refcnt += _Py_IMMORTAL_MINIMUM_REFCNT - 1;
2954+
}
2955+
return (void *)(refcnt - 2);
2956+
#else
2957+
return (void *)((refcnt -1) << 2);
2958+
#endif
2959+
}
2960+
#endif
2961+
29262962
/* Add op to the gcstate->trash_delete_later list. Called when the current
2927-
* call-stack depth gets large. op must be a gc'ed object, with refcount 0.
2928-
* Py_DECREF must already have been called on it.
2963+
* call-stack depth gets large. op must be a currently untracked gc'ed
2964+
* object, with refcount 0. Py_DECREF must already have been called on it.
29292965
*/
29302966
void
29312967
_PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
29322968
{
29332969
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
2934-
PyTypeObject *tp = Py_TYPE(op);
2935-
assert(tp->tp_flags & Py_TPFLAGS_HAVE_GC);
2936-
int tracked = 0;
2937-
if (tp->tp_is_gc == NULL || tp->tp_is_gc(op)) {
2938-
tracked = _PyObject_GC_IS_TRACKED(op);
2939-
if (tracked) {
2940-
_PyObject_GC_UNTRACK(op);
2941-
}
2942-
}
2943-
uintptr_t tagged_ptr = ((uintptr_t)tstate->delete_later) | tracked;
29442970
#ifdef Py_GIL_DISABLED
2945-
op->ob_tid = tagged_ptr;
2971+
op->ob_tid = (uintptr_t)tstate->delete_later;
29462972
#else
2947-
_Py_AS_GC(op)->_gc_next = tagged_ptr;
2973+
/* Store the delete_later pointer in the refcnt field. */
2974+
uintptr_t refcnt = pointer_to_safe_refcount(tstate->delete_later);
2975+
*((uintptr_t*)op) = refcnt;
2976+
assert(!_Py_IsImmortal(op));
29482977
#endif
29492978
tstate->delete_later = op;
29502979
}
@@ -2959,17 +2988,17 @@ _PyTrash_thread_destroy_chain(PyThreadState *tstate)
29592988
destructor dealloc = Py_TYPE(op)->tp_dealloc;
29602989

29612990
#ifdef Py_GIL_DISABLED
2962-
uintptr_t tagged_ptr = op->ob_tid;
2991+
tstate->delete_later = (PyObject*) op->ob_tid;
29632992
op->ob_tid = 0;
29642993
_Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, _Py_REF_MERGED);
29652994
#else
2966-
uintptr_t tagged_ptr = _Py_AS_GC(op)->_gc_next;
2967-
_Py_AS_GC(op)->_gc_next = 0;
2995+
/* Get the delete_later pointer from the refcnt field.
2996+
* See _PyTrash_thread_deposit_object(). */
2997+
uintptr_t refcnt = *((uintptr_t*)op);
2998+
tstate->delete_later = safe_refcount_to_pointer(refcnt);
2999+
op->ob_refcnt = 0;
29683000
#endif
2969-
tstate->delete_later = (PyObject *)(tagged_ptr & ~1);
2970-
if (tagged_ptr & 1) {
2971-
_PyObject_GC_TRACK(op);
2972-
}
3001+
29733002
/* Call the deallocator directly. This used to try to
29743003
* fool Py_DECREF into calling it indirectly, but
29753004
* Py_DECREF was already called on this object, and in
@@ -3043,11 +3072,10 @@ void
30433072
_Py_Dealloc(PyObject *op)
30443073
{
30453074
PyTypeObject *type = Py_TYPE(op);
3046-
unsigned long gc_flag = type->tp_flags & Py_TPFLAGS_HAVE_GC;
30473075
destructor dealloc = type->tp_dealloc;
30483076
PyThreadState *tstate = _PyThreadState_GET();
30493077
intptr_t margin = _Py_RecursionLimit_GetMargin(tstate);
3050-
if (margin < 2 && gc_flag) {
3078+
if (margin < 2) {
30513079
_PyTrash_thread_deposit_object(tstate, (PyObject *)op);
30523080
return;
30533081
}
@@ -3093,7 +3121,7 @@ _Py_Dealloc(PyObject *op)
30933121
Py_XDECREF(old_exc);
30943122
Py_DECREF(type);
30953123
#endif
3096-
if (tstate->delete_later && margin >= 4 && gc_flag) {
3124+
if (tstate->delete_later && margin >= 4) {
30973125
_PyTrash_thread_destroy_chain(tstate);
30983126
}
30993127
}

0 commit comments

Comments
 (0)