Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2060089

Browse files
authored
GH-135106: Restrict trashcan to GC'ed objects (GH-135682)
1 parent 39ea593 commit 2060089

File tree

2 files changed

+26
-52
lines changed

2 files changed

+26
-52
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Restrict the trashcan mechanism to GC'ed objects and untrack them while in
2+
the trashcan to prevent the GC and trashcan mechanisms conflicting.

Objects/object.c

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

30353035
/* Trashcan support. */
30363036

3037-
#ifndef Py_GIL_DISABLED
3038-
/* We need to store a pointer in the refcount field of
3039-
* an object. It is important that we never store 0 (NULL).
3040-
* It is also important to not make the object appear immortal,
3041-
* or it might be untracked by the cycle GC. */
3042-
static uintptr_t
3043-
pointer_to_safe_refcount(void *ptr)
3044-
{
3045-
uintptr_t full = (uintptr_t)ptr;
3046-
assert((full & 3) == 0);
3047-
#if SIZEOF_VOID_P > 4
3048-
uint32_t refcnt = (uint32_t)full;
3049-
if (refcnt >= (uint32_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
3050-
full = full - ((uintptr_t)_Py_IMMORTAL_MINIMUM_REFCNT) + 1;
3051-
}
3052-
return full + 2;
3053-
#else
3054-
// Make the top two bits 0, so it appears mortal.
3055-
return (full >> 2) + 1;
3056-
#endif
3057-
}
3058-
3059-
static void *
3060-
safe_refcount_to_pointer(uintptr_t refcnt)
3061-
{
3062-
#if SIZEOF_VOID_P > 4
3063-
if (refcnt & 1) {
3064-
refcnt += _Py_IMMORTAL_MINIMUM_REFCNT - 1;
3065-
}
3066-
return (void *)(refcnt - 2);
3067-
#else
3068-
return (void *)((refcnt -1) << 2);
3069-
#endif
3070-
}
3071-
#endif
3072-
30733037
/* Add op to the gcstate->trash_delete_later list. Called when the current
3074-
* call-stack depth gets large. op must be a currently untracked gc'ed
3075-
* object, with refcount 0. Py_DECREF must already have been called on it.
3038+
* call-stack depth gets large. op must be a gc'ed object, with refcount 0.
3039+
* Py_DECREF must already have been called on it.
30763040
*/
30773041
void
30783042
_PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
30793043
{
30803044
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
3045+
PyTypeObject *tp = Py_TYPE(op);
3046+
assert(tp->tp_flags & Py_TPFLAGS_HAVE_GC);
3047+
int tracked = 0;
3048+
if (tp->tp_is_gc == NULL || tp->tp_is_gc(op)) {
3049+
tracked = _PyObject_GC_IS_TRACKED(op);
3050+
if (tracked) {
3051+
_PyObject_GC_UNTRACK(op);
3052+
}
3053+
}
3054+
uintptr_t tagged_ptr = ((uintptr_t)tstate->delete_later) | tracked;
30813055
#ifdef Py_GIL_DISABLED
3082-
op->ob_tid = (uintptr_t)tstate->delete_later;
3056+
op->ob_tid = tagged_ptr;
30833057
#else
3084-
/* Store the delete_later pointer in the refcnt field. */
3085-
uintptr_t refcnt = pointer_to_safe_refcount(tstate->delete_later);
3086-
*((uintptr_t*)op) = refcnt;
3087-
assert(!_Py_IsImmortal(op));
3058+
_Py_AS_GC(op)->_gc_next = tagged_ptr;
30883059
#endif
30893060
tstate->delete_later = op;
30903061
}
@@ -3099,17 +3070,17 @@ _PyTrash_thread_destroy_chain(PyThreadState *tstate)
30993070
destructor dealloc = Py_TYPE(op)->tp_dealloc;
31003071

31013072
#ifdef Py_GIL_DISABLED
3102-
tstate->delete_later = (PyObject*) op->ob_tid;
3073+
uintptr_t tagged_ptr = op->ob_tid;
31033074
op->ob_tid = 0;
31043075
_Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, _Py_REF_MERGED);
31053076
#else
3106-
/* Get the delete_later pointer from the refcnt field.
3107-
* See _PyTrash_thread_deposit_object(). */
3108-
uintptr_t refcnt = *((uintptr_t*)op);
3109-
tstate->delete_later = safe_refcount_to_pointer(refcnt);
3110-
op->ob_refcnt = 0;
3077+
uintptr_t tagged_ptr = _Py_AS_GC(op)->_gc_next;
3078+
_Py_AS_GC(op)->_gc_next = 0;
31113079
#endif
3112-
3080+
tstate->delete_later = (PyObject *)(tagged_ptr & ~1);
3081+
if (tagged_ptr & 1) {
3082+
_PyObject_GC_TRACK(op);
3083+
}
31133084
/* Call the deallocator directly. This used to try to
31143085
* fool Py_DECREF into calling it indirectly, but
31153086
* Py_DECREF was already called on this object, and in
@@ -3183,10 +3154,11 @@ void
31833154
_Py_Dealloc(PyObject *op)
31843155
{
31853156
PyTypeObject *type = Py_TYPE(op);
3157+
unsigned long gc_flag = type->tp_flags & Py_TPFLAGS_HAVE_GC;
31863158
destructor dealloc = type->tp_dealloc;
31873159
PyThreadState *tstate = _PyThreadState_GET();
31883160
intptr_t margin = _Py_RecursionLimit_GetMargin(tstate);
3189-
if (margin < 2) {
3161+
if (margin < 2 && gc_flag) {
31903162
_PyTrash_thread_deposit_object(tstate, (PyObject *)op);
31913163
return;
31923164
}
@@ -3232,7 +3204,7 @@ _Py_Dealloc(PyObject *op)
32323204
Py_XDECREF(old_exc);
32333205
Py_DECREF(type);
32343206
#endif
3235-
if (tstate->delete_later && margin >= 4) {
3207+
if (tstate->delete_later && margin >= 4 && gc_flag) {
32363208
_PyTrash_thread_destroy_chain(tstate);
32373209
}
32383210
}

0 commit comments

Comments
 (0)