@@ -2225,8 +2225,6 @@ _Py_MergeZeroRefcount(PyObject *op)
22252225{
22262226 assert (_Py_atomic_load_uint32_relaxed (& op -> ob_ref_local ) == 0 );
22272227
2228- _Py_atomic_store_uintptr_relaxed (& op -> ob_tid , 0 );
2229-
22302228 Py_ssize_t refcount ;
22312229 for (;;) {
22322230 uint32_t shared = _Py_atomic_load_uint32_relaxed (& op -> ob_ref_shared );
@@ -2245,6 +2243,9 @@ _Py_MergeZeroRefcount(PyObject *op)
22452243 // count was temporarily negative and hasn't been proceessed yet.
22462244 // We don't want to merge it yet because that might result in the
22472245 // object being freed while it's still in the queue.
2246+ // We still need to zero the thread-id so that subsequent decrements
2247+ // from this thread do not push the ob_ref_local negative.
2248+ _Py_atomic_store_uintptr_relaxed (& op -> ob_tid , 0 );
22482249 break ;
22492250 }
22502251
@@ -2257,6 +2258,7 @@ _Py_MergeZeroRefcount(PyObject *op)
22572258 * a) weak references
22582259 * b) dangling pointers (e.g. loading from a list or dict)
22592260 */
2261+ _Py_atomic_store_uintptr_relaxed (& op -> ob_tid , 0 );
22602262 op -> ob_ref_local = 0 ;
22612263 PyObject_Del (op );
22622264 return ;
@@ -2272,6 +2274,7 @@ _Py_MergeZeroRefcount(PyObject *op)
22722274 }
22732275 }
22742276
2277+ _Py_atomic_store_uintptr_relaxed (& op -> ob_tid , 0 );
22752278 if (refcount == 0 ) {
22762279 _Py_Dealloc (op );
22772280 }
@@ -2355,12 +2358,15 @@ _Py_TryIncRefShared(PyObject *op)
23552358void
23562359_Py_DecRefShared (PyObject * op )
23572360{
2358- // TODO: fixme
23592361 uint32_t old_shared ;
23602362 uint32_t new_shared ;
2361- int ok ;
23622363
2363- do {
2364+ // We need to grab the thread-id before modifying the refcount
2365+ // because the owning thread may set it to zero if we mark the
2366+ // object as queued.
2367+ uintptr_t tid = _PyObject_ThreadId (op );
2368+
2369+ for (;;) {
23642370 old_shared = _Py_atomic_load_uint32_relaxed (& op -> ob_ref_shared );
23652371
23662372 new_shared = old_shared ;
@@ -2369,15 +2375,22 @@ _Py_DecRefShared(PyObject *op)
23692375 }
23702376 new_shared -= (1 << _Py_REF_SHARED_SHIFT );
23712377
2372- ok = _Py_atomic_compare_exchange_uint32 (
2378+ int ok = _Py_atomic_compare_exchange_uint32 (
23732379 & op -> ob_ref_shared ,
23742380 old_shared ,
23752381 new_shared );
2376- } while (!ok );
2382+
2383+ if (ok ) {
2384+ break ;
2385+ }
2386+ }
23772387
23782388 if (_Py_REF_IS_MERGED (new_shared )) {
23792389 // TOOD(sgross): implementation defined behavior
23802390 assert (((int32_t )new_shared ) >= 0 );
2391+ if (((int32_t )new_shared ) < 0 ) {
2392+ Py_FatalError ("negative refcount on merged object" );
2393+ }
23812394 }
23822395
23832396 if (_Py_REF_IS_QUEUED (new_shared ) != _Py_REF_IS_QUEUED (old_shared )) {
@@ -2389,7 +2402,7 @@ _Py_DecRefShared(PyObject *op)
23892402 }
23902403 }
23912404 else {
2392- _Py_queue_object (op );
2405+ _Py_queue_object (op , tid );
23932406 }
23942407 }
23952408 else if (_Py_REF_IS_MERGED (new_shared ) &&
0 commit comments