Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 87223f3

Browse filesBrowse files
jbradaricencukouerlend-aasland
authored
gh-103743: Add PyUnstable_Object_GC_NewWithExtraData (GH-103744)
Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
1 parent f6314b9 commit 87223f3
Copy full SHA for 87223f3

File tree

6 files changed

+156
-2
lines changed
Filter options

6 files changed

+156
-2
lines changed

‎Doc/c-api/gcsupport.rst

Copy file name to clipboardExpand all lines: Doc/c-api/gcsupport.rst
+20-1Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,31 @@ rules:
5959
Analogous to :c:func:`PyObject_New` but for container objects with the
6060
:const:`Py_TPFLAGS_HAVE_GC` flag set.
6161
62-
6362
.. c:function:: TYPE* PyObject_GC_NewVar(TYPE, PyTypeObject *type, Py_ssize_t size)
6463
6564
Analogous to :c:func:`PyObject_NewVar` but for container objects with the
6665
:const:`Py_TPFLAGS_HAVE_GC` flag set.
6766
67+
.. c:function:: PyObject* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
68+
69+
Analogous to :c:func:`PyObject_GC_New` but allocates *extra_size*
70+
bytes at the end of the object (at offset
71+
:c:member:`~PyTypeObject.tp_basicsize`).
72+
The allocated memory is initialized to zeros,
73+
except for the :c:type:`Python object header <PyObject>`.
74+
75+
The extra data will be deallocated with the object, but otherwise it is
76+
not managed by Python.
77+
78+
.. warning::
79+
The function is marked as unstable because the final mechanism
80+
for reserving extra data after an instance is not yet decided.
81+
For allocating a variable number of fields, prefer using
82+
:c:type:`PyVarObject` and :c:member:`~PyTypeObject.tp_itemsize`
83+
instead.
84+
85+
.. versionadded:: 3.12
86+
6887
6988
.. c:function:: TYPE* PyObject_GC_Resize(TYPE, PyVarObject *op, Py_ssize_t newsize)
7089

‎Include/cpython/objimpl.h

Copy file name to clipboardExpand all lines: Include/cpython/objimpl.h
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,6 @@ PyAPI_FUNC(int) PyObject_IS_GC(PyObject *obj);
9090
PyAPI_FUNC(int) PyType_SUPPORTS_WEAKREFS(PyTypeObject *type);
9191

9292
PyAPI_FUNC(PyObject **) PyObject_GET_WEAKREFS_LISTPTR(PyObject *op);
93+
94+
PyAPI_FUNC(PyObject *) PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *,
95+
size_t);

‎Lib/test/test_capi/test_misc.py

Copy file name to clipboardExpand all lines: Lib/test/test_capi/test_misc.py
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,20 @@ class dictsub(dict): ... # dict subclasses must work
10431043
self.assertEqual(_testcapi.function_get_kw_defaults(some), None)
10441044
self.assertEqual(some.__kwdefaults__, None)
10451045

1046+
def test_unstable_gc_new_with_extra_data(self):
1047+
class Data(_testcapi.ObjExtraData):
1048+
__slots__ = ('x', 'y')
1049+
1050+
d = Data()
1051+
d.x = 10
1052+
d.y = 20
1053+
d.extra = 30
1054+
self.assertEqual(d.x, 10)
1055+
self.assertEqual(d.y, 20)
1056+
self.assertEqual(d.extra, 30)
1057+
del d.extra
1058+
self.assertIsNone(d.extra)
1059+
10461060

10471061
class TestPendingCalls(unittest.TestCase):
10481062

+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyUnstable_Object_GC_NewWithExtraData` function that can be used to
2+
allocate additional memory after an object for data not managed by Python.

‎Modules/_testcapimodule.c

Copy file name to clipboardExpand all lines: Modules/_testcapimodule.c
+104-1Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3363,7 +3363,7 @@ test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
33633363
}
33643364
state.target = obj;
33653365
state.found = 0;
3366-
3366+
33673367
PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
33683368
Py_DECREF(obj);
33693369
if (!state.found) {
@@ -3400,6 +3400,98 @@ test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self),
34003400
Py_RETURN_NONE;
34013401
}
34023402

3403+
typedef struct {
3404+
PyObject_HEAD
3405+
} ObjExtraData;
3406+
3407+
static PyObject *
3408+
obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
3409+
{
3410+
size_t extra_size = sizeof(PyObject *);
3411+
PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size);
3412+
if (obj == NULL) {
3413+
return PyErr_NoMemory();
3414+
}
3415+
PyObject_GC_Track(obj);
3416+
return obj;
3417+
}
3418+
3419+
static PyObject **
3420+
obj_extra_data_get_extra_storage(PyObject *self)
3421+
{
3422+
return (PyObject **)((char *)self + Py_TYPE(self)->tp_basicsize);
3423+
}
3424+
3425+
static PyObject *
3426+
obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored))
3427+
{
3428+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3429+
PyObject *value = *extra_storage;
3430+
if (!value) {
3431+
Py_RETURN_NONE;
3432+
}
3433+
return Py_NewRef(value);
3434+
}
3435+
3436+
static int
3437+
obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored))
3438+
{
3439+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3440+
Py_CLEAR(*extra_storage);
3441+
if (newval) {
3442+
*extra_storage = Py_NewRef(newval);
3443+
}
3444+
return 0;
3445+
}
3446+
3447+
static PyGetSetDef obj_extra_data_getset[] = {
3448+
{"extra", (getter)obj_extra_data_get, (setter)obj_extra_data_set, NULL},
3449+
{NULL}
3450+
};
3451+
3452+
static int
3453+
obj_extra_data_traverse(PyObject *self, visitproc visit, void *arg)
3454+
{
3455+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3456+
PyObject *value = *extra_storage;
3457+
Py_VISIT(value);
3458+
return 0;
3459+
}
3460+
3461+
static int
3462+
obj_extra_data_clear(PyObject *self)
3463+
{
3464+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3465+
Py_CLEAR(*extra_storage);
3466+
return 0;
3467+
}
3468+
3469+
static void
3470+
obj_extra_data_dealloc(PyObject *self)
3471+
{
3472+
PyTypeObject *tp = Py_TYPE(self);
3473+
PyObject_GC_UnTrack(self);
3474+
obj_extra_data_clear(self);
3475+
tp->tp_free(self);
3476+
Py_DECREF(tp);
3477+
}
3478+
3479+
static PyType_Slot ObjExtraData_Slots[] = {
3480+
{Py_tp_getset, obj_extra_data_getset},
3481+
{Py_tp_dealloc, obj_extra_data_dealloc},
3482+
{Py_tp_traverse, obj_extra_data_traverse},
3483+
{Py_tp_clear, obj_extra_data_clear},
3484+
{Py_tp_new, obj_extra_data_new},
3485+
{Py_tp_free, PyObject_GC_Del},
3486+
{0, NULL},
3487+
};
3488+
3489+
static PyType_Spec ObjExtraData_TypeSpec = {
3490+
.name = "_testcapi.ObjExtraData",
3491+
.basicsize = sizeof(ObjExtraData),
3492+
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
3493+
.slots = ObjExtraData_Slots,
3494+
};
34033495

34043496
struct atexit_data {
34053497
int called;
@@ -4124,6 +4216,17 @@ PyInit__testcapi(void)
41244216
Py_INCREF(&MethStatic_Type);
41254217
PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type);
41264218

4219+
PyObject *ObjExtraData_Type = PyType_FromModuleAndSpec(
4220+
m, &ObjExtraData_TypeSpec, NULL);
4221+
if (ObjExtraData_Type == 0) {
4222+
return NULL;
4223+
}
4224+
int ret = PyModule_AddType(m, (PyTypeObject*)ObjExtraData_Type);
4225+
Py_DECREF(&ObjExtraData_Type);
4226+
if (ret < 0) {
4227+
return NULL;
4228+
}
4229+
41274230
PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX));
41284231
PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN));
41294232
PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX));

‎Modules/gcmodule.c

Copy file name to clipboardExpand all lines: Modules/gcmodule.c
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,6 +2367,19 @@ _PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems)
23672367
return op;
23682368
}
23692369

2370+
PyObject *
2371+
PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *tp, size_t extra_size)
2372+
{
2373+
size_t presize = _PyType_PreHeaderSize(tp);
2374+
PyObject *op = gc_alloc(_PyObject_SIZE(tp) + extra_size, presize);
2375+
if (op == NULL) {
2376+
return NULL;
2377+
}
2378+
memset(op, 0, _PyObject_SIZE(tp) + extra_size);
2379+
_PyObject_Init(op, tp);
2380+
return op;
2381+
}
2382+
23702383
PyVarObject *
23712384
_PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems)
23722385
{

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.