@@ -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 */
29302966void
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