Skip to content

Commit 6a480d8

Browse files
colesburyFullteaR
authored andcommittedNov 3, 2023
pythongh-110481: Implement biased reference counting (pythongh-110764)
1 parent 37fa7b1 commit 6a480d8

29 files changed

+511
-52
lines changed
 

‎Include/internal/pycore_long.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ _PyLong_FlipSign(PyLongObject *op) {
317317

318318
#define _PyLong_DIGIT_INIT(val) \
319319
{ \
320-
.ob_base = _PyObject_HEAD_INIT(&PyLong_Type) \
320+
.ob_base = _PyObject_HEAD_INIT(&PyLong_Type), \
321321
.long_value = { \
322322
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
323323
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \

‎Include/internal/pycore_object.h

+84-5
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,24 @@ PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *);
5454
Furthermore, we can't use designated initializers in Extensions since these
5555
are not supported pre-C++20. Thus, keeping an internal copy here is the most
5656
backwards compatible solution */
57+
#if defined(Py_NOGIL)
58+
#define _PyObject_HEAD_INIT(type) \
59+
{ \
60+
.ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL, \
61+
.ob_type = (type) \
62+
}
63+
#else
5764
#define _PyObject_HEAD_INIT(type) \
5865
{ \
5966
.ob_refcnt = _Py_IMMORTAL_REFCNT, \
6067
.ob_type = (type) \
61-
},
68+
}
69+
#endif
6270
#define _PyVarObject_HEAD_INIT(type, size) \
6371
{ \
64-
.ob_base = _PyObject_HEAD_INIT(type) \
72+
.ob_base = _PyObject_HEAD_INIT(type), \
6573
.ob_size = size \
66-
},
74+
}
6775

6876
extern void _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
6977
const char *func,
@@ -95,24 +103,63 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
95103
#ifdef Py_REF_DEBUG
96104
_Py_AddRefTotal(_PyInterpreterState_GET(), n);
97105
#endif
106+
#if !defined(Py_NOGIL)
98107
op->ob_refcnt += n;
108+
#else
109+
if (_Py_IsOwnedByCurrentThread(op)) {
110+
uint32_t local = op->ob_ref_local;
111+
Py_ssize_t refcnt = (Py_ssize_t)local + n;
112+
# if PY_SSIZE_T_MAX > UINT32_MAX
113+
if (refcnt > (Py_ssize_t)UINT32_MAX) {
114+
// Make the object immortal if the 32-bit local reference count
115+
// would overflow.
116+
refcnt = _Py_IMMORTAL_REFCNT_LOCAL;
117+
}
118+
# endif
119+
_Py_atomic_store_uint32_relaxed(&op->ob_ref_local, (uint32_t)refcnt);
120+
}
121+
else {
122+
_Py_atomic_add_ssize(&op->ob_ref_shared, (n << _Py_REF_SHARED_SHIFT));
123+
}
124+
#endif
99125
}
100126
#define _Py_RefcntAdd(op, n) _Py_RefcntAdd(_PyObject_CAST(op), n)
101127

102128
static inline void _Py_SetImmortal(PyObject *op)
103129
{
104130
if (op) {
131+
#ifdef Py_NOGIL
132+
op->ob_tid = _Py_UNOWNED_TID;
133+
op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL;
134+
op->ob_ref_shared = 0;
135+
#else
105136
op->ob_refcnt = _Py_IMMORTAL_REFCNT;
137+
#endif
106138
}
107139
}
108140
#define _Py_SetImmortal(op) _Py_SetImmortal(_PyObject_CAST(op))
109141

142+
// Makes an immortal object mortal again with the specified refcnt. Should only
143+
// be used during runtime finalization.
144+
static inline void _Py_SetMortal(PyObject *op, Py_ssize_t refcnt)
145+
{
146+
if (op) {
147+
assert(_Py_IsImmortal(op));
148+
#ifdef Py_NOGIL
149+
op->ob_tid = _Py_UNOWNED_TID;
150+
op->ob_ref_local = 0;
151+
op->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED);
152+
#else
153+
op->ob_refcnt = refcnt;
154+
#endif
155+
}
156+
}
157+
110158
/* _Py_ClearImmortal() should only be used during runtime finalization. */
111159
static inline void _Py_ClearImmortal(PyObject *op)
112160
{
113161
if (op) {
114-
assert(op->ob_refcnt == _Py_IMMORTAL_REFCNT);
115-
op->ob_refcnt = 1;
162+
_Py_SetMortal(op, 1);
116163
Py_DECREF(op);
117164
}
118165
}
@@ -122,6 +169,7 @@ static inline void _Py_ClearImmortal(PyObject *op)
122169
op = NULL; \
123170
} while (0)
124171

172+
#if !defined(Py_NOGIL)
125173
static inline void
126174
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
127175
{
@@ -161,6 +209,37 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
161209
#endif
162210
}
163211

212+
#else
213+
// TODO: implement Py_DECREF specializations for Py_NOGIL build
214+
static inline void
215+
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
216+
{
217+
Py_DECREF(op);
218+
}
219+
220+
static inline void
221+
_Py_DECREF_NO_DEALLOC(PyObject *op)
222+
{
223+
Py_DECREF(op);
224+
}
225+
226+
static inline int
227+
_Py_REF_IS_MERGED(Py_ssize_t ob_ref_shared)
228+
{
229+
return (ob_ref_shared & _Py_REF_SHARED_FLAG_MASK) == _Py_REF_MERGED;
230+
}
231+
232+
static inline int
233+
_Py_REF_IS_QUEUED(Py_ssize_t ob_ref_shared)
234+
{
235+
return (ob_ref_shared & _Py_REF_SHARED_FLAG_MASK) == _Py_REF_QUEUED;
236+
}
237+
238+
// Merge the local and shared reference count fields and add `extra` to the
239+
// refcount when merging.
240+
Py_ssize_t _Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra);
241+
#endif // !defined(Py_NOGIL)
242+
164243
#ifdef Py_REF_DEBUG
165244
# undef _Py_DEC_REFTOTAL
166245
#endif

‎Include/internal/pycore_runtime_init.h

+7-7
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,13 @@ extern PyTypeObject _PyExc_MemoryError;
129129
.latin1 = _Py_str_latin1_INIT, \
130130
}, \
131131
.tuple_empty = { \
132-
.ob_base = _PyVarObject_HEAD_INIT(&PyTuple_Type, 0) \
132+
.ob_base = _PyVarObject_HEAD_INIT(&PyTuple_Type, 0), \
133133
}, \
134134
.hamt_bitmap_node_empty = { \
135-
.ob_base = _PyVarObject_HEAD_INIT(&_PyHamt_BitmapNode_Type, 0) \
135+
.ob_base = _PyVarObject_HEAD_INIT(&_PyHamt_BitmapNode_Type, 0), \
136136
}, \
137137
.context_token_missing = { \
138-
.ob_base = _PyObject_HEAD_INIT(&_PyContextTokenMissing_Type) \
138+
.ob_base = _PyObject_HEAD_INIT(&_PyContextTokenMissing_Type), \
139139
}, \
140140
}, \
141141
}, \
@@ -172,11 +172,11 @@ extern PyTypeObject _PyExc_MemoryError;
172172
.singletons = { \
173173
._not_used = 1, \
174174
.hamt_empty = { \
175-
.ob_base = _PyObject_HEAD_INIT(&_PyHamt_Type) \
175+
.ob_base = _PyObject_HEAD_INIT(&_PyHamt_Type), \
176176
.h_root = (PyHamtNode*)&_Py_SINGLETON(hamt_bitmap_node_empty), \
177177
}, \
178178
.last_resort_memory_error = { \
179-
_PyObject_HEAD_INIT(&_PyExc_MemoryError) \
179+
_PyObject_HEAD_INIT(&_PyExc_MemoryError), \
180180
.args = (PyObject*)&_Py_SINGLETON(tuple_empty) \
181181
}, \
182182
}, \
@@ -206,7 +206,7 @@ extern PyTypeObject _PyExc_MemoryError;
206206

207207
#define _PyBytes_SIMPLE_INIT(CH, LEN) \
208208
{ \
209-
_PyVarObject_HEAD_INIT(&PyBytes_Type, (LEN)) \
209+
_PyVarObject_HEAD_INIT(&PyBytes_Type, (LEN)), \
210210
.ob_shash = -1, \
211211
.ob_sval = { (CH) }, \
212212
}
@@ -217,7 +217,7 @@ extern PyTypeObject _PyExc_MemoryError;
217217

218218
#define _PyUnicode_ASCII_BASE_INIT(LITERAL, ASCII) \
219219
{ \
220-
.ob_base = _PyObject_HEAD_INIT(&PyUnicode_Type) \
220+
.ob_base = _PyObject_HEAD_INIT(&PyUnicode_Type), \
221221
.length = sizeof(LITERAL) - 1, \
222222
.hash = -1, \
223223
.state = { \

0 commit comments

Comments
 (0)
Please sign in to comment.