Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Lib/test/test_capi.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,16 @@ def __del__(self):
del L
self.assertEqual(PyList.num, 0)

def test_subclass_of_base_that_uses_old_macros(self):
class Daisy(_testcapi.OldTrashcanMacros):
pass

DEPTH=150
daisyChain = Daisy()
for x in range(DEPTH):
daisyChain = (Daisy(), daisyChain, Daisy())
daisyChain = None

def test_subclass_of_heap_gc_ctype_with_tpdealloc_decrefs_once(self):
class HeapGcCTypeSubclass(_testcapi.HeapGcCType):
def __init__(self):
Expand Down
66 changes: 66 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -6011,6 +6011,67 @@ static PyTypeObject MyList_Type = {
MyList_new, /* tp_new */
};

/* create baseclass using the old trashcan macros (Py_TRASHCAN_SAFE_BEGIN/END) to test backwards compatibility */

typedef struct {
PyObject_HEAD
} OldTrashcanMacros;

static PyObject* OldTrashcanMacros_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
OldTrashcanMacros *self;
self = (OldTrashcanMacros*)type->tp_alloc(type, 0);
return (PyObject*)self;
}

static void OldTrashcanMacros_dealloc(OldTrashcanMacros* self)
{
PyObject_GC_UnTrack(self);
Py_TRASHCAN_SAFE_BEGIN(self)
Py_TYPE(self)->tp_free((PyObject*)self);
Py_TRASHCAN_SAFE_END(self)
}

PyTypeObject OldTrashcanMacros_Type = {
PyVarObject_HEAD_INIT(NULL,0)
"OldTrashcanMacros", /* tp_name */
(sizeof(OldTrashcanMacros)), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)OldTrashcanMacros_dealloc, /* tp-dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
"Old Trashcan Macros Test", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
OldTrashcanMacros_new, /* tp_new */
};

/* Test PEP 560 */

Expand Down Expand Up @@ -6641,6 +6702,11 @@ PyInit__testcapi(void)
Py_INCREF(&MyList_Type);
PyModule_AddObject(m, "MyList", (PyObject *)&MyList_Type);

if (PyType_Ready(&OldTrashcanMacros_Type) < 0)
return NULL;
Py_INCREF(&OldTrashcanMacros_Type);
PyModule_AddObject(m, "OldTrashcanMacros", (PyObject*)&OldTrashcanMacros_Type);

if (PyType_Ready(&MethodDescriptorBase_Type) < 0)
return NULL;
Py_INCREF(&MethodDescriptorBase_Type);
Expand Down
10 changes: 10 additions & 0 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,8 @@ subtype_dealloc(PyObject *self)
PyTypeObject *type, *base;
destructor basedealloc;
int has_finalizer;
PyThreadState *tstate = _PyThreadState_GET();
struct _gc_runtime_state *gcstate = &tstate->interp->gc;

/* Extract the type; we expect it to be a heap type */
type = Py_TYPE(self);
Expand Down Expand Up @@ -1283,7 +1285,11 @@ subtype_dealloc(PyObject *self)
/* UnTrack and re-Track around the trashcan macro, alas */
/* See explanation at end of function for full disclosure */
PyObject_GC_UnTrack(self);
++ tstate->trash_delete_nesting;
++ gcstate->trash_delete_nesting;
Py_TRASHCAN_BEGIN(self, subtype_dealloc);
-- tstate->trash_delete_nesting;
-- gcstate->trash_delete_nesting;

/* Find the nearest base with a different tp_dealloc */
base = type;
Expand Down Expand Up @@ -1378,7 +1384,11 @@ subtype_dealloc(PyObject *self)
Py_DECREF(type);

endlabel:
++ tstate->trash_delete_nesting;
++ gcstate->trash_delete_nesting;
Py_TRASHCAN_END
-- tstate->trash_delete_nesting;
-- gcstate->trash_delete_nesting;

/* Explanation of the weirdness around the trashcan macros:

Expand Down