@@ -40,6 +40,60 @@ static PyObject **nb_weaklist_ptr(PyObject *self) {
40
40
return weaklistoffset ? (PyObject **) ((uint8_t *) self + weaklistoffset) : nullptr ;
41
41
}
42
42
43
+ static void nb_enable_try_inc_ref (PyObject *obj) noexcept {
44
+ #if 0 && defined(Py_GIL_DISABLED) && PY_VERSION_HEX >= 0x030E00A5
45
+ PyUnstable_EnableTryIncRef(obj);
46
+ #elif defined(Py_GIL_DISABLED)
47
+ // Since this is called during object construction, we know that we have
48
+ // the only reference to the object and can use a non-atomic write.
49
+ assert(obj->ob_ref_shared == 0);
50
+ obj->ob_ref_shared = _Py_REF_MAYBE_WEAKREF;
51
+ #endif
52
+ }
53
+
54
+ static bool nb_try_inc_ref (PyObject *obj) noexcept {
55
+ #if 0 && defined(Py_GIL_DISABLED) && PY_VERSION_HEX >= 0x030E00A5
56
+ return PyUnstable_TryIncRef(obj);
57
+ #elif defined(Py_GIL_DISABLED)
58
+ // See https://github.com/python/cpython/blob/d05140f9f77d7dfc753dd1e5ac3a5962aaa03eff/Include/internal/pycore_object.h#L761
59
+ uint32_t local = _Py_atomic_load_uint32_relaxed(&obj->ob_ref_local);
60
+ local += 1;
61
+ if (local == 0) {
62
+ // immortal
63
+ return true;
64
+ }
65
+ if (_Py_IsOwnedByCurrentThread(obj)) {
66
+ _Py_atomic_store_uint32_relaxed(&obj->ob_ref_local, local);
67
+ #ifdef Py_REF_DEBUG
68
+ _Py_INCREF_IncRefTotal();
69
+ #endif
70
+ return true;
71
+ }
72
+ Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared);
73
+ for (;;) {
74
+ // If the shared refcount is zero and the object is either merged
75
+ // or may not have weak references, then we cannot incref it.
76
+ if (shared == 0 || shared == _Py_REF_MERGED) {
77
+ return false;
78
+ }
79
+
80
+ if (_Py_atomic_compare_exchange_ssize(
81
+ &obj->ob_ref_shared, &shared, shared + (1 << _Py_REF_SHARED_SHIFT))) {
82
+ #ifdef Py_REF_DEBUG
83
+ _Py_INCREF_IncRefTotal();
84
+ #endif
85
+ return true;
86
+ }
87
+ }
88
+ #else
89
+ if (Py_REFCNT (obj) > 0 ) {
90
+ Py_INCREF (obj);
91
+ return true ;
92
+ }
93
+ return false ;
94
+ #endif
95
+ }
96
+
43
97
static PyGetSetDef inst_getset[] = {
44
98
{ " __dict__" , PyObject_GenericGetDict, PyObject_GenericSetDict, nullptr , nullptr },
45
99
{ nullptr , nullptr , nullptr , nullptr , nullptr }
@@ -98,6 +152,7 @@ PyObject *inst_new_int(PyTypeObject *tp, PyObject * /* args */,
98
152
self->clear_keep_alive = 0 ;
99
153
self->intrusive = intrusive;
100
154
self->unused = 0 ;
155
+ nb_enable_try_inc_ref ((PyObject *)self);
101
156
102
157
// Update hash table that maps from C++ to Python instance
103
158
nb_shard &shard = internals->shard ((void *) payload);
@@ -163,6 +218,7 @@ PyObject *inst_new_ext(PyTypeObject *tp, void *value) {
163
218
self->clear_keep_alive = 0 ;
164
219
self->intrusive = intrusive;
165
220
self->unused = 0 ;
221
+ nb_enable_try_inc_ref ((PyObject *)self);
166
222
167
223
nb_shard &shard = internals->shard (value);
168
224
lock_shard guard (shard);
@@ -1766,16 +1822,18 @@ PyObject *nb_type_put(const std::type_info *cpp_type,
1766
1822
PyTypeObject *tp = Py_TYPE (seq.inst );
1767
1823
1768
1824
if (nb_type_data (tp)->type == cpp_type) {
1769
- Py_INCREF (seq.inst );
1770
- return seq.inst ;
1825
+ if (nb_try_inc_ref (seq.inst )) {
1826
+ return seq.inst ;
1827
+ }
1771
1828
}
1772
1829
1773
1830
if (!lookup_type ())
1774
1831
return nullptr ;
1775
1832
1776
1833
if (PyType_IsSubtype (tp, td->type_py )) {
1777
- Py_INCREF (seq.inst );
1778
- return seq.inst ;
1834
+ if (nb_try_inc_ref (seq.inst )) {
1835
+ return seq.inst ;
1836
+ }
1779
1837
}
1780
1838
1781
1839
if (seq.next == nullptr )
0 commit comments