@@ -376,6 +376,157 @@ static inline void _PyObject_GC_UNTRACK(
376376 _PyObject_GC_UNTRACK(__FILE__, __LINE__, _PyObject_CAST(op))
377377#endif
378378
379+ #ifdef Py_GIL_DISABLED
380+
381+ /* Tries to increment an object's reference count
382+ *
383+ * This is a specialized version of _Py_TryIncref that only succeeds if the
384+ * object is immortal or local to this thread. It does not handle the case
385+ * where the reference count modification requires an atomic operation. This
386+ * allows call sites to specialize for the immortal/local case.
387+ */
388+ static inline Py_ALWAYS_INLINE int
389+ _Py_TryIncrefFast (PyObject * op ) {
390+ uint32_t local = _Py_atomic_load_uint32_relaxed (& op -> ob_ref_local );
391+ local += 1 ;
392+ if (local == 0 ) {
393+ // immortal
394+ return 1 ;
395+ }
396+ if (_Py_IsOwnedByCurrentThread (op )) {
397+ _Py_atomic_store_uint32_relaxed (& op -> ob_ref_local , local );
398+ #ifdef Py_REF_DEBUG
399+ _Py_IncRefTotal (_PyInterpreterState_GET ());
400+ #endif
401+ return 1 ;
402+ }
403+ return 0 ;
404+ }
405+
406+ static inline Py_ALWAYS_INLINE int
407+ _Py_TryIncRefShared (PyObject * op )
408+ {
409+ for (;;) {
410+ Py_ssize_t shared = _Py_atomic_load_ssize_relaxed (& op -> ob_ref_shared );
411+
412+ // If the shared refcount is zero and the object is either merged
413+ // or may not have weak references, then we cannot incref it.
414+ if (shared == 0 || shared == _Py_REF_MERGED ) {
415+ return 0 ;
416+ }
417+
418+ if (_Py_atomic_compare_exchange_ssize (
419+ & op -> ob_ref_shared ,
420+ & shared ,
421+ shared + (1 << _Py_REF_SHARED_SHIFT ))) {
422+ #ifdef Py_REF_DEBUG
423+ _Py_IncRefTotal (_PyInterpreterState_GET ());
424+ #endif
425+ return 1 ;
426+ }
427+ }
428+ }
429+
430+ /* Tries to incref the object op and ensures that *src still points to it. */
431+ static inline int
432+ _Py_TryAcquireObject (PyObject * * src , PyObject * op )
433+ {
434+ if (_Py_TryIncrefFast (op )) {
435+ return 1 ;
436+ }
437+ if (!_Py_TryIncRefShared (op )) {
438+ return 0 ;
439+ }
440+ if (op != _Py_atomic_load_ptr (src )) {
441+ Py_DECREF (op );
442+ return 0 ;
443+ }
444+ return 1 ;
445+ }
446+
447+ /* Loads and increfs an object from ptr, which may contain a NULL value.
448+ Safe with concurrent (atomic) updates to ptr.
449+ NOTE: The writer must set maybe-weakref on the stored object! */
450+ static inline Py_ALWAYS_INLINE PyObject *
451+ _Py_XFetchRef (PyObject * * ptr )
452+ {
453+ #ifdef Py_NOGIL
454+ for (;;) {
455+ PyObject * value = _Py_atomic_load_ptr (ptr );
456+ if (value == NULL ) {
457+ return value ;
458+ }
459+ if (_Py_TryAcquireObject (ptr , value )) {
460+ return value ;
461+ }
462+ }
463+ #else
464+ return Py_XNewRef (* ptr );
465+ #endif
466+ }
467+
468+ /* Attempts to loads and increfs an object from ptr. Returns NULL
469+ on failure, which may be due to a NULL value or a concurrent update. */
470+ static inline Py_ALWAYS_INLINE PyObject *
471+ _Py_TryXFetchRef (PyObject * * ptr )
472+ {
473+ PyObject * value = _Py_atomic_load_ptr (ptr );
474+ if (value == NULL ) {
475+ return value ;
476+ }
477+ if (_Py_TryAcquireObject (ptr , value )) {
478+ return value ;
479+ }
480+ return NULL ;
481+ }
482+
483+ /* Like Py_NewRef but also optimistically sets _Py_REF_MAYBE_WEAKREF
484+ on objects owned by a different thread. */
485+ static inline PyObject *
486+ _Py_NewRefWithLock (PyObject * op )
487+ {
488+ _Py_INCREF_STAT_INC ();
489+ uint32_t local = _Py_atomic_load_uint32_relaxed (& op -> ob_ref_local );
490+ local += 1 ;
491+ if (local == 0 ) {
492+ return op ;
493+ }
494+
495+ #ifdef Py_REF_DEBUG
496+ _Py_IncRefTotal (_PyInterpreterState_GET ());
497+ #endif
498+ if (_Py_IsOwnedByCurrentThread (op )) {
499+ _Py_atomic_store_uint32_relaxed (& op -> ob_ref_local , local );
500+ }
501+ else {
502+ for (;;) {
503+ Py_ssize_t shared = _Py_atomic_load_ssize_relaxed (& op -> ob_ref_shared );
504+ Py_ssize_t new_shared = shared + (1 << _Py_REF_SHARED_SHIFT );
505+ if ((shared & _Py_REF_SHARED_FLAG_MASK ) == 0 ) {
506+ new_shared |= _Py_REF_MAYBE_WEAKREF ;
507+ }
508+ if (_Py_atomic_compare_exchange_ssize (
509+ & op -> ob_ref_shared ,
510+ & shared ,
511+ new_shared )) {
512+ return op ;
513+ }
514+ }
515+ }
516+ return op ;
517+ }
518+
519+ static inline PyObject *
520+ _Py_XNewRefWithLock (PyObject * obj )
521+ {
522+ if (obj == NULL ) {
523+ return NULL ;
524+ }
525+ return _Py_NewRefWithLock (obj );
526+ }
527+
528+ #endif
529+
379530#ifdef Py_REF_DEBUG
380531extern void _PyInterpreterState_FinalizeRefTotal (PyInterpreterState * );
381532extern void _Py_FinalizeRefTotal (_PyRuntimeState * );
0 commit comments