Skip to content

Commit c86a112

Browse files
authored
bpo-39573: Add Py_SET_REFCNT() function (GH-18389)
Add a Py_SET_REFCNT() function to set the reference counter of an object.
1 parent a93c51e commit c86a112

File tree

7 files changed

+30
-14
lines changed

7 files changed

+30
-14
lines changed

Doc/c-api/structures.rst

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ the definition of all other Python objects.
7979
(((PyObject*)(o))->ob_refcnt)
8080

8181

82+
.. c:function:: void Py_SET_REFCNT(PyObject *o, Py_ssize_t refcnt)
83+
84+
Set the object *o* reference counter to *refcnt*.
85+
86+
.. versionadded:: 3.9
87+
88+
8289
.. c:macro:: Py_SIZE(o)
8390
8491
This macro is used to access the :attr:`ob_size` member of a Python object.

Include/object.h

+5
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ typedef struct {
123123
#define Py_TYPE(ob) (_PyObject_CAST(ob)->ob_type)
124124
#define Py_SIZE(ob) (_PyVarObject_CAST(ob)->ob_size)
125125

126+
static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
127+
ob->ob_refcnt = refcnt;
128+
}
129+
#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt)
130+
126131
/*
127132
Type objects contain a string containing the type name (to help somewhat
128133
in debugging), the allocation parameters (see PyObject_New() and
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add a :c:func:`Py_SET_REFCNT` function to set the reference counter of an
2+
object.

Modules/_testcapimodule.c

+8-7
Original file line numberDiff line numberDiff line change
@@ -3551,7 +3551,7 @@ slot_tp_del(PyObject *self)
35513551

35523552
/* Temporarily resurrect the object. */
35533553
assert(Py_REFCNT(self) == 0);
3554-
Py_REFCNT(self) = 1;
3554+
Py_SET_REFCNT(self, 1);
35553555

35563556
/* Save the current exception, if any. */
35573557
PyErr_Fetch(&error_type, &error_value, &error_traceback);
@@ -3574,7 +3574,8 @@ slot_tp_del(PyObject *self)
35743574
* cause a recursive call.
35753575
*/
35763576
assert(Py_REFCNT(self) > 0);
3577-
if (--Py_REFCNT(self) == 0) {
3577+
Py_SET_REFCNT(self, Py_REFCNT(self) - 1);
3578+
if (Py_REFCNT(self) == 0) {
35783579
/* this is the normal path out */
35793580
return;
35803581
}
@@ -3585,7 +3586,7 @@ slot_tp_del(PyObject *self)
35853586
{
35863587
Py_ssize_t refcnt = Py_REFCNT(self);
35873588
_Py_NewReference(self);
3588-
Py_REFCNT(self) = refcnt;
3589+
Py_SET_REFCNT(self, refcnt);
35893590
}
35903591
assert(!PyType_IS_GC(Py_TYPE(self)) || _PyObject_GC_IS_TRACKED(self));
35913592
/* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased
@@ -4621,7 +4622,7 @@ check_pyobject_uninitialized_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
46214622
return NULL;
46224623
}
46234624
/* Initialize reference count to avoid early crash in ceval or GC */
4624-
Py_REFCNT(op) = 1;
4625+
Py_SET_REFCNT(op, 1);
46254626
/* object fields like ob_type are uninitialized! */
46264627
return test_pyobject_is_freed("check_pyobject_uninitialized_is_freed", op);
46274628
}
@@ -4636,7 +4637,7 @@ check_pyobject_forbidden_bytes_is_freed(PyObject *self, PyObject *Py_UNUSED(args
46364637
return NULL;
46374638
}
46384639
/* Initialize reference count to avoid early crash in ceval or GC */
4639-
Py_REFCNT(op) = 1;
4640+
Py_SET_REFCNT(op, 1);
46404641
/* ob_type field is after the memory block: part of "forbidden bytes"
46414642
when using debug hooks on memory allocators! */
46424643
return test_pyobject_is_freed("check_pyobject_forbidden_bytes_is_freed", op);
@@ -4652,7 +4653,7 @@ check_pyobject_freed_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
46524653
}
46534654
Py_TYPE(op)->tp_dealloc(op);
46544655
/* Reset reference count to avoid early crash in ceval or GC */
4655-
Py_REFCNT(op) = 1;
4656+
Py_SET_REFCNT(op, 1);
46564657
/* object memory is freed! */
46574658
return test_pyobject_is_freed("check_pyobject_freed_is_freed", op);
46584659
}
@@ -5134,7 +5135,7 @@ negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
51345135
}
51355136
assert(Py_REFCNT(obj) == 1);
51365137

5137-
Py_REFCNT(obj) = 0;
5138+
Py_SET_REFCNT(obj, 0);
51385139
/* Py_DECREF() must call _Py_NegativeRefcount() and abort Python */
51395140
Py_DECREF(obj);
51405141

Objects/moduleobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ PyModuleDef_Init(struct PyModuleDef* def)
5151
return NULL;
5252
if (def->m_base.m_index == 0) {
5353
max_module_number++;
54-
Py_REFCNT(def) = 1;
54+
Py_SET_REFCNT(def, 1);
5555
Py_TYPE(def) = &PyModuleDef_Type;
5656
def->m_base.m_index = max_module_number;
5757
}

Objects/object.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
213213
}
214214

215215
/* Temporarily resurrect the object. */
216-
Py_REFCNT(self) = 1;
216+
Py_SET_REFCNT(self, 1);
217217

218218
PyObject_CallFinalizer(self);
219219

@@ -223,15 +223,16 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
223223

224224
/* Undo the temporary resurrection; can't use DECREF here, it would
225225
* cause a recursive call. */
226-
if (--Py_REFCNT(self) == 0) {
226+
Py_SET_REFCNT(self, Py_REFCNT(self) - 1);
227+
if (Py_REFCNT(self) == 0) {
227228
return 0; /* this is the normal path out */
228229
}
229230

230231
/* tp_finalize resurrected it! Make it look like the original Py_DECREF
231232
* never happened. */
232233
Py_ssize_t refcnt = Py_REFCNT(self);
233234
_Py_NewReference(self);
234-
Py_REFCNT(self) = refcnt;
235+
Py_SET_REFCNT(self, refcnt);
235236

236237
_PyObject_ASSERT(self,
237238
(!PyType_IS_GC(Py_TYPE(self))
@@ -1818,7 +1819,7 @@ _Py_NewReference(PyObject *op)
18181819
#ifdef Py_REF_DEBUG
18191820
_Py_RefTotal++;
18201821
#endif
1821-
Py_REFCNT(op) = 1;
1822+
Py_SET_REFCNT(op, 1);
18221823
#ifdef Py_TRACE_REFS
18231824
_Py_AddToAllObjects(op, 1);
18241825
#endif

Objects/unicodeobject.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1903,7 +1903,7 @@ unicode_dealloc(PyObject *unicode)
19031903

19041904
case SSTATE_INTERNED_MORTAL:
19051905
/* revive dead object temporarily for DelItem */
1906-
Py_REFCNT(unicode) = 3;
1906+
Py_SET_REFCNT(unicode, 3);
19071907
if (PyDict_DelItem(interned, unicode) != 0) {
19081908
_PyErr_WriteUnraisableMsg("deletion of interned string failed",
19091909
NULL);
@@ -15367,7 +15367,7 @@ PyUnicode_InternInPlace(PyObject **p)
1536715367
}
1536815368
/* The two references in interned are not counted by refcnt.
1536915369
The deallocator will take care of this */
15370-
Py_REFCNT(s) -= 2;
15370+
Py_SET_REFCNT(s, Py_REFCNT(s) - 2);
1537115371
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;
1537215372
}
1537315373

0 commit comments

Comments
 (0)