summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorPetr Viktorin <encukou@gmail.com>2019-09-12 09:44:46 (GMT)
committerGitHub <noreply@github.com>2019-09-12 09:44:46 (GMT)
commit3562ae25402aad36583bc27d4d82c67554323d5e (patch)
treebf0b4ffda88bfeaf218446ee972ec3bbd9e70d3b /Modules
parenteb19c45298361d75f744dcda295fa635a8490c3c (diff)
downloadcpython-3562ae25402aad36583bc27d4d82c67554323d5e.zip
cpython-3562ae25402aad36583bc27d4d82c67554323d5e.tar.gz
cpython-3562ae25402aad36583bc27d4d82c67554323d5e.tar.bz2
[3.8] bpo-37879: Suppress subtype_dealloc decref when base type is a C heap type (GH-15323, GH-16004) (GH-15966)
The instance destructor for a type is responsible for preparing an instance for deallocation by decrementing the reference counts of its referents. If an instance belongs to a heap type, the type object of an instance has its reference count decremented while for static types, which are permanently allocated, the type object is unaffected by the instance destructor. Previously, the default instance destructor searched the class hierarchy for an inherited instance destructor and, if present, would invoke it. Then, if the instance type is a heap type, it would decrement the reference count of that heap type. However, this could result in the premature destruction of a type because the inherited instance destructor should have already decremented the reference count of the type object. This change avoids the premature destruction of the type object by suppressing the decrement of its reference count when an inherited, non-default instance destructor has been invoked. Finally, an assertion on the Py_SIZE of a type was deleted. Heap types have a non zero size, making this into an incorrect assertion. https://github.com/python/cpython/pull/15323. (cherry picked from commit ff023ed36ea260ab64be5895f1f1f087c798987a) Fixup: https://github.com/python/cpython/pull/16004. (cherry picked from commit 5e9caeec76119a0d61c25f1466c27b7dbd5115bd) Co-authored-by: Eddie Elizondo <eduardo.elizondorueda@gmail.com>
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_testcapimodule.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 15bfe26..cb9206c 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -32,6 +32,8 @@
# error "_testcapi must test the public Python C API, not CPython internal C API"
#endif
+static struct PyModuleDef _testcapimodule;
+
static PyObject *TestError; /* set to exception object in init */
/* Raise TestError with test_name + ": " + msg, and return NULL. */
@@ -5955,6 +5957,190 @@ static PyTypeObject MethodDescriptor2_Type = {
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | _Py_TPFLAGS_HAVE_VECTORCALL,
};
+PyDoc_STRVAR(heapgctype__doc__,
+"A heap type with GC, and with overridden dealloc.\n\n"
+"The 'value' attribute is set to 10 in __init__.");
+
+typedef struct {
+ PyObject_HEAD
+ int value;
+} HeapCTypeObject;
+
+static struct PyMemberDef heapctype_members[] = {
+ {"value", T_INT, offsetof(HeapCTypeObject, value)},
+ {NULL} /* Sentinel */
+};
+
+static int
+heapctype_init(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ ((HeapCTypeObject *)self)->value = 10;
+ return 0;
+}
+
+static void
+heapgcctype_dealloc(HeapCTypeObject *self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ PyObject_GC_UnTrack(self);
+ PyObject_GC_Del(self);
+ Py_DECREF(tp);
+}
+
+static PyType_Slot HeapGcCType_slots[] = {
+ {Py_tp_init, heapctype_init},
+ {Py_tp_members, heapctype_members},
+ {Py_tp_dealloc, heapgcctype_dealloc},
+ {Py_tp_doc, (char*)heapgctype__doc__},
+ {0, 0},
+};
+
+static PyType_Spec HeapGcCType_spec = {
+ "_testcapi.HeapGcCType",
+ sizeof(HeapCTypeObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+ HeapGcCType_slots
+};
+
+PyDoc_STRVAR(heapctype__doc__,
+"A heap type without GC, but with overridden dealloc.\n\n"
+"The 'value' attribute is set to 10 in __init__.");
+
+static void
+heapctype_dealloc(HeapCTypeObject *self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ PyObject_Del(self);
+ Py_DECREF(tp);
+}
+
+static PyType_Slot HeapCType_slots[] = {
+ {Py_tp_init, heapctype_init},
+ {Py_tp_members, heapctype_members},
+ {Py_tp_dealloc, heapctype_dealloc},
+ {Py_tp_doc, (char*)heapctype__doc__},
+ {0, 0},
+};
+
+static PyType_Spec HeapCType_spec = {
+ "_testcapi.HeapCType",
+ sizeof(HeapCTypeObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ HeapCType_slots
+};
+
+PyDoc_STRVAR(heapctypesubclass__doc__,
+"Subclass of HeapCType, without GC.\n\n"
+"__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
+
+typedef struct {
+ HeapCTypeObject base;
+ int value2;
+} HeapCTypeSubclassObject;
+
+static int
+heapctypesubclass_init(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ /* Call __init__ of the superclass */
+ if (heapctype_init(self, args, kwargs) < 0) {
+ return -1;
+ }
+ /* Initialize additional element */
+ ((HeapCTypeSubclassObject *)self)->value2 = 20;
+ return 0;
+}
+
+static struct PyMemberDef heapctypesubclass_members[] = {
+ {"value2", T_INT, offsetof(HeapCTypeSubclassObject, value2)},
+ {NULL} /* Sentinel */
+};
+
+static PyType_Slot HeapCTypeSubclass_slots[] = {
+ {Py_tp_init, heapctypesubclass_init},
+ {Py_tp_members, heapctypesubclass_members},
+ {Py_tp_doc, (char*)heapctypesubclass__doc__},
+ {0, 0},
+};
+
+static PyType_Spec HeapCTypeSubclass_spec = {
+ "_testcapi.HeapCTypeSubclass",
+ sizeof(HeapCTypeSubclassObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ HeapCTypeSubclass_slots
+};
+
+PyDoc_STRVAR(heapctypesubclasswithfinalizer__doc__,
+"Subclass of HeapCType with a finalizer that reassigns __class__.\n\n"
+"__class__ is set to plain HeapCTypeSubclass during finalization.\n"
+"__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
+
+static int
+heapctypesubclasswithfinalizer_init(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyTypeObject *base = (PyTypeObject *)PyType_GetSlot(Py_TYPE(self), Py_tp_base);
+ initproc base_init = PyType_GetSlot(base, Py_tp_init);
+ base_init(self, args, kwargs);
+ return 0;
+}
+
+static void
+heapctypesubclasswithfinalizer_finalize(PyObject *self)
+{
+ PyObject *error_type, *error_value, *error_traceback, *m;
+ PyObject *oldtype = NULL, *newtype = NULL;
+
+ /* Save the current exception, if any. */
+ PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+ m = PyState_FindModule(&_testcapimodule);
+ if (m == NULL) {
+ goto cleanup_finalize;
+ }
+ oldtype = PyObject_GetAttrString(m, "HeapCTypeSubclassWithFinalizer");
+ newtype = PyObject_GetAttrString(m, "HeapCTypeSubclass");
+ if (oldtype == NULL || newtype == NULL) {
+ goto cleanup_finalize;
+ }
+
+ if (PyObject_SetAttrString(self, "__class__", newtype) < 0) {
+ goto cleanup_finalize;
+ }
+ if (PyObject_SetAttrString(
+ oldtype, "refcnt_in_del", PyLong_FromSsize_t(Py_REFCNT(oldtype))) < 0) {
+ goto cleanup_finalize;
+ }
+ if (PyObject_SetAttrString(
+ newtype, "refcnt_in_del", PyLong_FromSsize_t(Py_REFCNT(newtype))) < 0) {
+ goto cleanup_finalize;
+ }
+
+cleanup_finalize:
+ Py_XDECREF(oldtype);
+ Py_XDECREF(newtype);
+
+ /* Restore the saved exception. */
+ PyErr_Restore(error_type, error_value, error_traceback);
+}
+
+static PyType_Slot HeapCTypeSubclassWithFinalizer_slots[] = {
+ {Py_tp_init, heapctypesubclasswithfinalizer_init},
+ {Py_tp_members, heapctypesubclass_members},
+ {Py_tp_finalize, heapctypesubclasswithfinalizer_finalize},
+ {Py_tp_doc, (char*)heapctypesubclasswithfinalizer__doc__},
+ {0, 0},
+};
+
+static PyType_Spec HeapCTypeSubclassWithFinalizer_spec = {
+ "_testcapi.HeapCTypeSubclassWithFinalizer",
+ sizeof(HeapCTypeSubclassObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE,
+ HeapCTypeSubclassWithFinalizer_slots
+};
+
static struct PyModuleDef _testcapimodule = {
PyModuleDef_HEAD_INIT,
@@ -6089,5 +6275,40 @@ PyInit__testcapi(void)
TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
Py_INCREF(TestError);
PyModule_AddObject(m, "error", TestError);
+
+ PyObject *HeapGcCType = PyType_FromSpec(&HeapGcCType_spec);
+ if (HeapGcCType == NULL) {
+ return NULL;
+ }
+ PyModule_AddObject(m, "HeapGcCType", HeapGcCType);
+
+ PyObject *HeapCType = PyType_FromSpec(&HeapCType_spec);
+ if (HeapCType == NULL) {
+ return NULL;
+ }
+ PyObject *subclass_bases = PyTuple_Pack(1, HeapCType);
+ if (subclass_bases == NULL) {
+ return NULL;
+ }
+ PyObject *HeapCTypeSubclass = PyType_FromSpecWithBases(&HeapCTypeSubclass_spec, subclass_bases);
+ if (HeapCTypeSubclass == NULL) {
+ return NULL;
+ }
+ Py_DECREF(subclass_bases);
+ PyModule_AddObject(m, "HeapCTypeSubclass", HeapCTypeSubclass);
+
+ PyObject *subclass_with_finalizer_bases = PyTuple_Pack(1, HeapCTypeSubclass);
+ if (subclass_with_finalizer_bases == NULL) {
+ return NULL;
+ }
+ PyObject *HeapCTypeSubclassWithFinalizer = PyType_FromSpecWithBases(
+ &HeapCTypeSubclassWithFinalizer_spec, subclass_with_finalizer_bases);
+ if (HeapCTypeSubclassWithFinalizer == NULL) {
+ return NULL;
+ }
+ Py_DECREF(subclass_with_finalizer_bases);
+ PyModule_AddObject(m, "HeapCTypeSubclassWithFinalizer", HeapCTypeSubclassWithFinalizer);
+
+ PyState_AddModule(m, &_testcapimodule);
return m;
}