Skip to content

Commit 045e349

Browse files
[3.14] pythongh-140061: Use _PyObject_IsUniquelyReferenced() to check if objects are uniquely referenced (pythongh-140062) (pythongh-140157)
The previous `Py_REFCNT(x) == 1` checks can have data races in the free threaded build. `_PyObject_IsUniquelyReferenced(x)` is a more conservative check that is safe in the free threaded build and is identical to `Py_REFCNT(x) == 1` in the default GIL-enabled build. (cherry picked from commit 32c2649) Co-authored-by: Sergey Miryanov <[email protected]>
1 parent 6b94c7c commit 045e349

File tree

10 files changed

+29
-36
lines changed

10 files changed

+29
-36
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixing the checking of whether an object is uniquely referenced to ensure
2+
free-threaded compatibility. Patch by Sergey Miryanov.

Modules/_elementtree.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
912912
return Py_NewRef(object);
913913
}
914914

915-
if (Py_REFCNT(object) == 1) {
915+
if (_PyObject_IsUniquelyReferenced(object)) {
916916
if (PyDict_CheckExact(object)) {
917917
PyObject *key, *value;
918918
Py_ssize_t pos = 0;
@@ -2794,8 +2794,9 @@ treebuilder_handle_data(TreeBuilderObject* self, PyObject* data)
27942794
self->data = Py_NewRef(data);
27952795
} else {
27962796
/* more than one item; use a list to collect items */
2797-
if (PyBytes_CheckExact(self->data) && Py_REFCNT(self->data) == 1 &&
2798-
PyBytes_CheckExact(data) && PyBytes_GET_SIZE(data) == 1) {
2797+
if (PyBytes_CheckExact(self->data)
2798+
&& _PyObject_IsUniquelyReferenced(self->data)
2799+
&& PyBytes_CheckExact(data) && PyBytes_GET_SIZE(data) == 1) {
27992800
/* XXX this code path unused in Python 3? */
28002801
/* expat often generates single character data sections; handle
28012802
the most common case by resizing the existing string... */

Modules/_functoolsmodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
298298
if (kw == NULL) {
299299
pto->kw = PyDict_New();
300300
}
301-
else if (Py_REFCNT(kw) == 1) {
301+
else if (_PyObject_IsUniquelyReferenced(kw)) {
302302
pto->kw = Py_NewRef(kw);
303303
}
304304
else {
@@ -995,7 +995,7 @@ _functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq,
995995
for (;;) {
996996
PyObject *op2;
997997

998-
if (Py_REFCNT(args) > 1) {
998+
if (!_PyObject_IsUniquelyReferenced(args)) {
999999
Py_DECREF(args);
10001000
if ((args = PyTuple_New(2)) == NULL)
10011001
goto Fail;

Modules/itertoolsmodule.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ pairwise_next(PyObject *op)
376376
}
377377

378378
result = po->result;
379-
if (Py_REFCNT(result) == 1) {
379+
if (_PyObject_IsUniquelyReferenced(result)) {
380380
Py_INCREF(result);
381381
PyObject *last_old = PyTuple_GET_ITEM(result, 0);
382382
PyObject *last_new = PyTuple_GET_ITEM(result, 1);
@@ -802,7 +802,7 @@ teedataobject_traverse(PyObject *op, visitproc visit, void * arg)
802802
static void
803803
teedataobject_safe_decref(PyObject *obj)
804804
{
805-
while (obj && Py_REFCNT(obj) == 1) {
805+
while (obj && _PyObject_IsUniquelyReferenced(obj)) {
806806
teedataobject *tmp = teedataobject_CAST(obj);
807807
PyObject *nextlink = tmp->nextlink;
808808
tmp->nextlink = NULL;
@@ -2114,7 +2114,7 @@ product_next(PyObject *op)
21142114
Py_ssize_t *indices = lz->indices;
21152115

21162116
/* Copy the previous result tuple or re-use it if available */
2117-
if (Py_REFCNT(result) > 1) {
2117+
if (!_PyObject_IsUniquelyReferenced(result)) {
21182118
PyObject *old_result = result;
21192119
result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), npools);
21202120
if (result == NULL)
@@ -2343,7 +2343,7 @@ combinations_next(PyObject *op)
23432343
}
23442344
} else {
23452345
/* Copy the previous result tuple or re-use it if available */
2346-
if (Py_REFCNT(result) > 1) {
2346+
if (!_PyObject_IsUniquelyReferenced(result)) {
23472347
PyObject *old_result = result;
23482348
result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r);
23492349
if (result == NULL)
@@ -2589,7 +2589,7 @@ cwr_next(PyObject *op)
25892589
}
25902590
} else {
25912591
/* Copy the previous result tuple or re-use it if available */
2592-
if (Py_REFCNT(result) > 1) {
2592+
if (!_PyObject_IsUniquelyReferenced(result)) {
25932593
PyObject *old_result = result;
25942594
result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r);
25952595
if (result == NULL)
@@ -2850,7 +2850,7 @@ permutations_next(PyObject *op)
28502850
goto empty;
28512851

28522852
/* Copy the previous result tuple or re-use it if available */
2853-
if (Py_REFCNT(result) > 1) {
2853+
if (!_PyObject_IsUniquelyReferenced(result)) {
28542854
PyObject *old_result = result;
28552855
result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r);
28562856
if (result == NULL)
@@ -3818,7 +3818,7 @@ zip_longest_next(PyObject *op)
38183818
return NULL;
38193819
if (lz->numactive == 0)
38203820
return NULL;
3821-
if (Py_REFCNT(result) == 1) {
3821+
if (_PyObject_IsUniquelyReferenced(result)) {
38223822
Py_INCREF(result);
38233823
for (i=0 ; i < tuplesize ; i++) {
38243824
it = PyTuple_GET_ITEM(lz->ittuple, i);

Objects/bytesobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3147,7 +3147,7 @@ PyBytes_Concat(PyObject **pv, PyObject *w)
31473147
return;
31483148
}
31493149

3150-
if (Py_REFCNT(*pv) == 1 && PyBytes_CheckExact(*pv)) {
3150+
if (_PyObject_IsUniquelyReferenced(*pv) && PyBytes_CheckExact(*pv)) {
31513151
/* Only one reference, so we can resize in place */
31523152
Py_ssize_t oldsize;
31533153
Py_buffer wb;
@@ -3232,7 +3232,7 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
32323232
Py_DECREF(v);
32333233
return 0;
32343234
}
3235-
if (Py_REFCNT(v) != 1) {
3235+
if (!_PyObject_IsUniquelyReferenced(v)) {
32363236
if (oldsize < newsize) {
32373237
*pv = _PyBytes_FromSize(newsize, 0);
32383238
if (*pv) {

Objects/dictobject.c

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5618,22 +5618,10 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self,
56185618

56195619
#endif
56205620

5621-
static bool
5622-
has_unique_reference(PyObject *op)
5623-
{
5624-
#ifdef Py_GIL_DISABLED
5625-
return (_Py_IsOwnedByCurrentThread(op) &&
5626-
op->ob_ref_local == 1 &&
5627-
_Py_atomic_load_ssize_relaxed(&op->ob_ref_shared) == 0);
5628-
#else
5629-
return Py_REFCNT(op) == 1;
5630-
#endif
5631-
}
5632-
56335621
static bool
56345622
acquire_iter_result(PyObject *result)
56355623
{
5636-
if (has_unique_reference(result)) {
5624+
if (_PyObject_IsUniquelyReferenced(result)) {
56375625
Py_INCREF(result);
56385626
return true;
56395627
}
@@ -5782,7 +5770,7 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self)
57825770
}
57835771
else if (Py_IS_TYPE(di, &PyDictRevIterItem_Type)) {
57845772
result = di->di_result;
5785-
if (Py_REFCNT(result) == 1) {
5773+
if (_PyObject_IsUniquelyReferenced(result)) {
57865774
PyObject *oldkey = PyTuple_GET_ITEM(result, 0);
57875775
PyObject *oldvalue = PyTuple_GET_ITEM(result, 1);
57885776
PyTuple_SET_ITEM(result, 0, Py_NewRef(key));

Objects/longobject.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ _PyLong_Negate(PyLongObject **x_p)
324324
PyLongObject *x;
325325

326326
x = (PyLongObject *)*x_p;
327-
if (Py_REFCNT(x) == 1) {
327+
if (_PyObject_IsUniquelyReferenced((PyObject *)x)) {
328328
_PyLong_FlipSign(x);
329329
return;
330330
}
@@ -5754,7 +5754,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)
57545754
assert(size_a >= 0);
57555755
_PyLong_SetSignAndDigitCount(c, 1, size_a);
57565756
}
5757-
else if (Py_REFCNT(a) == 1) {
5757+
else if (_PyObject_IsUniquelyReferenced((PyObject *)a)) {
57585758
c = (PyLongObject*)Py_NewRef(a);
57595759
}
57605760
else {
@@ -5768,7 +5768,8 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)
57685768
assert(size_a >= 0);
57695769
_PyLong_SetSignAndDigitCount(d, 1, size_a);
57705770
}
5771-
else if (Py_REFCNT(b) == 1 && size_a <= alloc_b) {
5771+
else if (_PyObject_IsUniquelyReferenced((PyObject *)b)
5772+
&& size_a <= alloc_b) {
57725773
d = (PyLongObject*)Py_NewRef(b);
57735774
assert(size_a >= 0);
57745775
_PyLong_SetSignAndDigitCount(d, 1, size_a);

Objects/setobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,7 +2428,7 @@ set_init(PyObject *so, PyObject *args, PyObject *kwds)
24282428
if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable))
24292429
return -1;
24302430

2431-
if (Py_REFCNT(self) == 1 && self->fill == 0) {
2431+
if (_PyObject_IsUniquelyReferenced((PyObject *)self) && self->fill == 0) {
24322432
self->hash = -1;
24332433
if (iterable == NULL) {
24342434
return 0;
@@ -2758,7 +2758,7 @@ int
27582758
PySet_Add(PyObject *anyset, PyObject *key)
27592759
{
27602760
if (!PySet_Check(anyset) &&
2761-
(!PyFrozenSet_Check(anyset) || Py_REFCNT(anyset) != 1)) {
2761+
(!PyFrozenSet_Check(anyset) || !_PyObject_IsUniquelyReferenced(anyset))) {
27622762
PyErr_BadInternalCall();
27632763
return -1;
27642764
}

Objects/tupleobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ int
118118
PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem)
119119
{
120120
PyObject **p;
121-
if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) {
121+
if (!PyTuple_Check(op) || !_PyObject_IsUniquelyReferenced(op)) {
122122
Py_XDECREF(newitem);
123123
PyErr_BadInternalCall();
124124
return -1;
@@ -923,7 +923,7 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
923923

924924
v = (PyTupleObject *) *pv;
925925
if (v == NULL || !Py_IS_TYPE(v, &PyTuple_Type) ||
926-
(Py_SIZE(v) != 0 && Py_REFCNT(v) != 1)) {
926+
(Py_SIZE(v) != 0 && !_PyObject_IsUniquelyReferenced(*pv))) {
927927
*pv = 0;
928928
Py_XDECREF(v);
929929
PyErr_BadInternalCall();

Python/marshal.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "pycore_code.h" // _PyCode_New()
1212
#include "pycore_hashtable.h" // _Py_hashtable_t
1313
#include "pycore_long.h" // _PyLong_IsZero()
14+
#include "pycore_object.h" // _PyObject_IsUniquelyReferenced
1415
#include "pycore_pystate.h" // _PyInterpreterState_GET()
1516
#include "pycore_setobject.h" // _PySet_NextEntryRef()
1617
#include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal()
@@ -388,7 +389,7 @@ w_ref(PyObject *v, char *flag, WFILE *p)
388389
* But we use TYPE_REF always for interned string, to PYC file stable
389390
* as possible.
390391
*/
391-
if (Py_REFCNT(v) == 1 &&
392+
if (_PyObject_IsUniquelyReferenced(v) &&
392393
!(PyUnicode_CheckExact(v) && PyUnicode_CHECK_INTERNED(v))) {
393394
return 0;
394395
}

0 commit comments

Comments
 (0)