mirror of
https://github.com/python/cpython
synced 2024-10-14 18:49:33 +00:00
gh-93649: Split gc- and allocation tests from _testcapimodule.c (GH-104403)
This commit is contained in:
parent
b2c1b4da19
commit
19ee53d52e
|
@ -169,7 +169,7 @@
|
||||||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
||||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
|
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
|
||||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c
|
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c
|
||||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||||
|
|
||||||
# Some testing modules MUST be built as shared libraries.
|
# Some testing modules MUST be built as shared libraries.
|
||||||
|
|
344
Modules/_testcapi/gc.c
Normal file
344
Modules/_testcapi/gc.c
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
#include "parts.h"
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
test_gc_control(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
int orig_enabled = PyGC_IsEnabled();
|
||||||
|
const char* msg = "ok";
|
||||||
|
int old_state;
|
||||||
|
|
||||||
|
old_state = PyGC_Enable();
|
||||||
|
msg = "Enable(1)";
|
||||||
|
if (old_state != orig_enabled) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(1)";
|
||||||
|
if (!PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_state = PyGC_Disable();
|
||||||
|
msg = "disable(2)";
|
||||||
|
if (!old_state) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(2)";
|
||||||
|
if (PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_state = PyGC_Enable();
|
||||||
|
msg = "enable(3)";
|
||||||
|
if (old_state) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(3)";
|
||||||
|
if (!PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!orig_enabled) {
|
||||||
|
old_state = PyGC_Disable();
|
||||||
|
msg = "disable(4)";
|
||||||
|
if (old_state) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(4)";
|
||||||
|
if (PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
/* Try to clean up if we can. */
|
||||||
|
if (orig_enabled) {
|
||||||
|
PyGC_Enable();
|
||||||
|
} else {
|
||||||
|
PyGC_Disable();
|
||||||
|
}
|
||||||
|
PyErr_Format(PyExc_ValueError, "GC control failed in %s", msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
without_gc(PyObject *Py_UNUSED(self), PyObject *obj)
|
||||||
|
{
|
||||||
|
PyTypeObject *tp = (PyTypeObject*)obj;
|
||||||
|
if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
|
||||||
|
return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj);
|
||||||
|
}
|
||||||
|
if (PyType_IS_GC(tp)) {
|
||||||
|
// Don't try this at home, kids:
|
||||||
|
tp->tp_flags -= Py_TPFLAGS_HAVE_GC;
|
||||||
|
tp->tp_free = PyObject_Del;
|
||||||
|
tp->tp_traverse = NULL;
|
||||||
|
tp->tp_clear = NULL;
|
||||||
|
}
|
||||||
|
assert(!PyType_IS_GC(tp));
|
||||||
|
return Py_NewRef(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
slot_tp_del(PyObject *self)
|
||||||
|
{
|
||||||
|
PyObject *del, *res;
|
||||||
|
|
||||||
|
/* Temporarily resurrect the object. */
|
||||||
|
assert(Py_REFCNT(self) == 0);
|
||||||
|
Py_SET_REFCNT(self, 1);
|
||||||
|
|
||||||
|
/* Save the current exception, if any. */
|
||||||
|
PyObject *exc = PyErr_GetRaisedException();
|
||||||
|
|
||||||
|
PyObject *tp_del = PyUnicode_InternFromString("__tp_del__");
|
||||||
|
if (tp_del == NULL) {
|
||||||
|
PyErr_WriteUnraisable(NULL);
|
||||||
|
PyErr_SetRaisedException(exc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Execute __del__ method, if any. */
|
||||||
|
del = _PyType_Lookup(Py_TYPE(self), tp_del);
|
||||||
|
Py_DECREF(tp_del);
|
||||||
|
if (del != NULL) {
|
||||||
|
res = PyObject_CallOneArg(del, self);
|
||||||
|
if (res == NULL)
|
||||||
|
PyErr_WriteUnraisable(del);
|
||||||
|
else
|
||||||
|
Py_DECREF(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore the saved exception. */
|
||||||
|
PyErr_SetRaisedException(exc);
|
||||||
|
|
||||||
|
/* Undo the temporary resurrection; can't use DECREF here, it would
|
||||||
|
* cause a recursive call.
|
||||||
|
*/
|
||||||
|
assert(Py_REFCNT(self) > 0);
|
||||||
|
Py_SET_REFCNT(self, Py_REFCNT(self) - 1);
|
||||||
|
if (Py_REFCNT(self) == 0) {
|
||||||
|
/* this is the normal path out */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* __del__ resurrected it! Make it look like the original Py_DECREF
|
||||||
|
* never happened.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
Py_ssize_t refcnt = Py_REFCNT(self);
|
||||||
|
_Py_NewReferenceNoTotal(self);
|
||||||
|
Py_SET_REFCNT(self, refcnt);
|
||||||
|
}
|
||||||
|
assert(!PyType_IS_GC(Py_TYPE(self)) || PyObject_GC_IsTracked(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
with_tp_del(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *obj;
|
||||||
|
PyTypeObject *tp;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O:with_tp_del", &obj))
|
||||||
|
return NULL;
|
||||||
|
tp = (PyTypeObject *) obj;
|
||||||
|
if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"heap type expected, got %R", obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
tp->tp_del = slot_tp_del;
|
||||||
|
return Py_NewRef(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct gc_visit_state_basic {
|
||||||
|
PyObject *target;
|
||||||
|
int found;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
gc_visit_callback_basic(PyObject *obj, void *arg)
|
||||||
|
{
|
||||||
|
struct gc_visit_state_basic *state = (struct gc_visit_state_basic *)arg;
|
||||||
|
if (obj == state->target) {
|
||||||
|
state->found = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
|
||||||
|
PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject *obj;
|
||||||
|
struct gc_visit_state_basic state;
|
||||||
|
|
||||||
|
obj = PyList_New(0);
|
||||||
|
if (obj == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
state.target = obj;
|
||||||
|
state.found = 0;
|
||||||
|
|
||||||
|
PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
|
||||||
|
Py_DECREF(obj);
|
||||||
|
if (!state.found) {
|
||||||
|
PyErr_SetString(
|
||||||
|
PyExc_AssertionError,
|
||||||
|
"test_gc_visit_objects_basic: Didn't find live list");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
gc_visit_callback_exit_early(PyObject *obj, void *arg)
|
||||||
|
{
|
||||||
|
int *visited_i = (int *)arg;
|
||||||
|
(*visited_i)++;
|
||||||
|
if (*visited_i == 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self),
|
||||||
|
PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
int visited_i = 0;
|
||||||
|
PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i);
|
||||||
|
if (visited_i != 2) {
|
||||||
|
PyErr_SetString(
|
||||||
|
PyExc_AssertionError,
|
||||||
|
"test_gc_visit_objects_exit_early: did not exit when expected");
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
} ObjExtraData;
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
size_t extra_size = sizeof(PyObject *);
|
||||||
|
PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size);
|
||||||
|
if (obj == NULL) {
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
PyObject_GC_Track(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject **
|
||||||
|
obj_extra_data_get_extra_storage(PyObject *self)
|
||||||
|
{
|
||||||
|
return (PyObject **)((char *)self + Py_TYPE(self)->tp_basicsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
||||||
|
PyObject *value = *extra_storage;
|
||||||
|
if (!value) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
return Py_NewRef(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
||||||
|
Py_CLEAR(*extra_storage);
|
||||||
|
if (newval) {
|
||||||
|
*extra_storage = Py_NewRef(newval);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyGetSetDef obj_extra_data_getset[] = {
|
||||||
|
{"extra", (getter)obj_extra_data_get, (setter)obj_extra_data_set, NULL},
|
||||||
|
{NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
obj_extra_data_traverse(PyObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
||||||
|
PyObject *value = *extra_storage;
|
||||||
|
Py_VISIT(value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
obj_extra_data_clear(PyObject *self)
|
||||||
|
{
|
||||||
|
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
||||||
|
Py_CLEAR(*extra_storage);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
obj_extra_data_dealloc(PyObject *self)
|
||||||
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(self);
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
|
obj_extra_data_clear(self);
|
||||||
|
tp->tp_free(self);
|
||||||
|
Py_DECREF(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyType_Slot ObjExtraData_Slots[] = {
|
||||||
|
{Py_tp_getset, obj_extra_data_getset},
|
||||||
|
{Py_tp_dealloc, obj_extra_data_dealloc},
|
||||||
|
{Py_tp_traverse, obj_extra_data_traverse},
|
||||||
|
{Py_tp_clear, obj_extra_data_clear},
|
||||||
|
{Py_tp_new, obj_extra_data_new},
|
||||||
|
{Py_tp_free, PyObject_GC_Del},
|
||||||
|
{0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyType_Spec ObjExtraData_TypeSpec = {
|
||||||
|
.name = "_testcapi.ObjExtraData",
|
||||||
|
.basicsize = sizeof(ObjExtraData),
|
||||||
|
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
||||||
|
.slots = ObjExtraData_Slots,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyMethodDef test_methods[] = {
|
||||||
|
{"test_gc_control", test_gc_control, METH_NOARGS},
|
||||||
|
{"test_gc_visit_objects_basic", test_gc_visit_objects_basic, METH_NOARGS, NULL},
|
||||||
|
{"test_gc_visit_objects_exit_early", test_gc_visit_objects_exit_early, METH_NOARGS, NULL},
|
||||||
|
{"without_gc", without_gc, METH_O, NULL},
|
||||||
|
{"with_tp_del", with_tp_del, METH_VARARGS, NULL},
|
||||||
|
{NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int _PyTestCapi_Init_GC(PyObject *mod)
|
||||||
|
{
|
||||||
|
if (PyModule_AddFunctions(mod, test_methods) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (PyModule_AddFunctions(mod, test_methods) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *ObjExtraData_Type = PyType_FromModuleAndSpec(
|
||||||
|
mod, &ObjExtraData_TypeSpec, NULL);
|
||||||
|
if (ObjExtraData_Type == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int ret = PyModule_AddType(mod, (PyTypeObject*)ObjExtraData_Type);
|
||||||
|
Py_DECREF(ObjExtraData_Type);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ int _PyTestCapi_Init_Code(PyObject *module);
|
||||||
int _PyTestCapi_Init_Buffer(PyObject *module);
|
int _PyTestCapi_Init_Buffer(PyObject *module);
|
||||||
int _PyTestCapi_Init_PyOS(PyObject *module);
|
int _PyTestCapi_Init_PyOS(PyObject *module);
|
||||||
int _PyTestCapi_Init_Immortal(PyObject *module);
|
int _PyTestCapi_Init_Immortal(PyObject *module);
|
||||||
|
int _PyTestCapi_Init_GC(PyObject *mod);
|
||||||
|
|
||||||
#ifdef LIMITED_API_AVAILABLE
|
#ifdef LIMITED_API_AVAILABLE
|
||||||
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
||||||
|
|
|
@ -154,68 +154,6 @@ test_sizeof_c_types(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
test_gc_control(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|
||||||
{
|
|
||||||
int orig_enabled = PyGC_IsEnabled();
|
|
||||||
const char* msg = "ok";
|
|
||||||
int old_state;
|
|
||||||
|
|
||||||
old_state = PyGC_Enable();
|
|
||||||
msg = "Enable(1)";
|
|
||||||
if (old_state != orig_enabled) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
msg = "IsEnabled(1)";
|
|
||||||
if (!PyGC_IsEnabled()) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
old_state = PyGC_Disable();
|
|
||||||
msg = "disable(2)";
|
|
||||||
if (!old_state) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
msg = "IsEnabled(2)";
|
|
||||||
if (PyGC_IsEnabled()) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
old_state = PyGC_Enable();
|
|
||||||
msg = "enable(3)";
|
|
||||||
if (old_state) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
msg = "IsEnabled(3)";
|
|
||||||
if (!PyGC_IsEnabled()) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!orig_enabled) {
|
|
||||||
old_state = PyGC_Disable();
|
|
||||||
msg = "disable(4)";
|
|
||||||
if (old_state) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
msg = "IsEnabled(4)";
|
|
||||||
if (PyGC_IsEnabled()) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
|
|
||||||
failed:
|
|
||||||
/* Try to clean up if we can. */
|
|
||||||
if (orig_enabled) {
|
|
||||||
PyGC_Enable();
|
|
||||||
} else {
|
|
||||||
PyGC_Disable();
|
|
||||||
}
|
|
||||||
PyErr_Format(TestError, "GC control failed in %s", msg);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored))
|
test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
{
|
{
|
||||||
|
@ -1627,95 +1565,6 @@ restore_crossinterp_data(PyObject *self, PyObject *args)
|
||||||
return _PyCrossInterpreterData_NewObject(data);
|
return _PyCrossInterpreterData_NewObject(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
slot_tp_del(PyObject *self)
|
|
||||||
{
|
|
||||||
PyObject *del, *res;
|
|
||||||
|
|
||||||
/* Temporarily resurrect the object. */
|
|
||||||
assert(Py_REFCNT(self) == 0);
|
|
||||||
Py_SET_REFCNT(self, 1);
|
|
||||||
|
|
||||||
/* Save the current exception, if any. */
|
|
||||||
PyObject *exc = PyErr_GetRaisedException();
|
|
||||||
|
|
||||||
PyObject *tp_del = PyUnicode_InternFromString("__tp_del__");
|
|
||||||
if (tp_del == NULL) {
|
|
||||||
PyErr_WriteUnraisable(NULL);
|
|
||||||
PyErr_SetRaisedException(exc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Execute __del__ method, if any. */
|
|
||||||
del = _PyType_Lookup(Py_TYPE(self), tp_del);
|
|
||||||
Py_DECREF(tp_del);
|
|
||||||
if (del != NULL) {
|
|
||||||
res = PyObject_CallOneArg(del, self);
|
|
||||||
if (res == NULL)
|
|
||||||
PyErr_WriteUnraisable(del);
|
|
||||||
else
|
|
||||||
Py_DECREF(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore the saved exception. */
|
|
||||||
PyErr_SetRaisedException(exc);
|
|
||||||
|
|
||||||
/* Undo the temporary resurrection; can't use DECREF here, it would
|
|
||||||
* cause a recursive call.
|
|
||||||
*/
|
|
||||||
assert(Py_REFCNT(self) > 0);
|
|
||||||
Py_SET_REFCNT(self, Py_REFCNT(self) - 1);
|
|
||||||
if (Py_REFCNT(self) == 0) {
|
|
||||||
/* this is the normal path out */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* __del__ resurrected it! Make it look like the original Py_DECREF
|
|
||||||
* never happened.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
Py_ssize_t refcnt = Py_REFCNT(self);
|
|
||||||
_Py_NewReferenceNoTotal(self);
|
|
||||||
Py_SET_REFCNT(self, refcnt);
|
|
||||||
}
|
|
||||||
assert(!PyType_IS_GC(Py_TYPE(self)) || PyObject_GC_IsTracked(self));
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
with_tp_del(PyObject *self, PyObject *args)
|
|
||||||
{
|
|
||||||
PyObject *obj;
|
|
||||||
PyTypeObject *tp;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O:with_tp_del", &obj))
|
|
||||||
return NULL;
|
|
||||||
tp = (PyTypeObject *) obj;
|
|
||||||
if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"heap type expected, got %R", obj);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
tp->tp_del = slot_tp_del;
|
|
||||||
return Py_NewRef(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
without_gc(PyObject *Py_UNUSED(self), PyObject *obj)
|
|
||||||
{
|
|
||||||
PyTypeObject *tp = (PyTypeObject*)obj;
|
|
||||||
if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
|
|
||||||
return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj);
|
|
||||||
}
|
|
||||||
if (PyType_IS_GC(tp)) {
|
|
||||||
// Don't try this at home, kids:
|
|
||||||
tp->tp_flags -= Py_TPFLAGS_HAVE_GC;
|
|
||||||
tp->tp_free = PyObject_Del;
|
|
||||||
tp->tp_traverse = NULL;
|
|
||||||
tp->tp_clear = NULL;
|
|
||||||
}
|
|
||||||
assert(!PyType_IS_GC(tp));
|
|
||||||
return Py_NewRef(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyMethodDef ml;
|
static PyMethodDef ml;
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -3342,165 +3191,6 @@ function_set_kw_defaults(PyObject *self, PyObject *args)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gc_visit_state_basic {
|
|
||||||
PyObject *target;
|
|
||||||
int found;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
gc_visit_callback_basic(PyObject *obj, void *arg)
|
|
||||||
{
|
|
||||||
struct gc_visit_state_basic *state = (struct gc_visit_state_basic *)arg;
|
|
||||||
if (obj == state->target) {
|
|
||||||
state->found = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
|
|
||||||
PyObject *Py_UNUSED(ignored))
|
|
||||||
{
|
|
||||||
PyObject *obj;
|
|
||||||
struct gc_visit_state_basic state;
|
|
||||||
|
|
||||||
obj = PyList_New(0);
|
|
||||||
if (obj == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
state.target = obj;
|
|
||||||
state.found = 0;
|
|
||||||
|
|
||||||
PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
|
|
||||||
Py_DECREF(obj);
|
|
||||||
if (!state.found) {
|
|
||||||
PyErr_SetString(
|
|
||||||
PyExc_AssertionError,
|
|
||||||
"test_gc_visit_objects_basic: Didn't find live list");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
gc_visit_callback_exit_early(PyObject *obj, void *arg)
|
|
||||||
{
|
|
||||||
int *visited_i = (int *)arg;
|
|
||||||
(*visited_i)++;
|
|
||||||
if (*visited_i == 2) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self),
|
|
||||||
PyObject *Py_UNUSED(ignored))
|
|
||||||
{
|
|
||||||
int visited_i = 0;
|
|
||||||
PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i);
|
|
||||||
if (visited_i != 2) {
|
|
||||||
PyErr_SetString(
|
|
||||||
PyExc_AssertionError,
|
|
||||||
"test_gc_visit_objects_exit_early: did not exit when expected");
|
|
||||||
}
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD
|
|
||||||
} ObjExtraData;
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
||||||
{
|
|
||||||
size_t extra_size = sizeof(PyObject *);
|
|
||||||
PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size);
|
|
||||||
if (obj == NULL) {
|
|
||||||
return PyErr_NoMemory();
|
|
||||||
}
|
|
||||||
PyObject_GC_Track(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject **
|
|
||||||
obj_extra_data_get_extra_storage(PyObject *self)
|
|
||||||
{
|
|
||||||
return (PyObject **)((char *)self + Py_TYPE(self)->tp_basicsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored))
|
|
||||||
{
|
|
||||||
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
|
||||||
PyObject *value = *extra_storage;
|
|
||||||
if (!value) {
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
return Py_NewRef(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored))
|
|
||||||
{
|
|
||||||
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
|
||||||
Py_CLEAR(*extra_storage);
|
|
||||||
if (newval) {
|
|
||||||
*extra_storage = Py_NewRef(newval);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyGetSetDef obj_extra_data_getset[] = {
|
|
||||||
{"extra", (getter)obj_extra_data_get, (setter)obj_extra_data_set, NULL},
|
|
||||||
{NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
obj_extra_data_traverse(PyObject *self, visitproc visit, void *arg)
|
|
||||||
{
|
|
||||||
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
|
||||||
PyObject *value = *extra_storage;
|
|
||||||
Py_VISIT(value);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
obj_extra_data_clear(PyObject *self)
|
|
||||||
{
|
|
||||||
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
|
|
||||||
Py_CLEAR(*extra_storage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
obj_extra_data_dealloc(PyObject *self)
|
|
||||||
{
|
|
||||||
PyTypeObject *tp = Py_TYPE(self);
|
|
||||||
PyObject_GC_UnTrack(self);
|
|
||||||
obj_extra_data_clear(self);
|
|
||||||
tp->tp_free(self);
|
|
||||||
Py_DECREF(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyType_Slot ObjExtraData_Slots[] = {
|
|
||||||
{Py_tp_getset, obj_extra_data_getset},
|
|
||||||
{Py_tp_dealloc, obj_extra_data_dealloc},
|
|
||||||
{Py_tp_traverse, obj_extra_data_traverse},
|
|
||||||
{Py_tp_clear, obj_extra_data_clear},
|
|
||||||
{Py_tp_new, obj_extra_data_new},
|
|
||||||
{Py_tp_free, PyObject_GC_Del},
|
|
||||||
{0, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
static PyType_Spec ObjExtraData_TypeSpec = {
|
|
||||||
.name = "_testcapi.ObjExtraData",
|
|
||||||
.basicsize = sizeof(ObjExtraData),
|
|
||||||
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
|
||||||
.slots = ObjExtraData_Slots,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct atexit_data {
|
struct atexit_data {
|
||||||
int called;
|
int called;
|
||||||
};
|
};
|
||||||
|
@ -3538,7 +3228,6 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"set_errno", set_errno, METH_VARARGS},
|
{"set_errno", set_errno, METH_VARARGS},
|
||||||
{"test_config", test_config, METH_NOARGS},
|
{"test_config", test_config, METH_NOARGS},
|
||||||
{"test_sizeof_c_types", test_sizeof_c_types, METH_NOARGS},
|
{"test_sizeof_c_types", test_sizeof_c_types, METH_NOARGS},
|
||||||
{"test_gc_control", test_gc_control, METH_NOARGS},
|
|
||||||
{"test_list_api", test_list_api, METH_NOARGS},
|
{"test_list_api", test_list_api, METH_NOARGS},
|
||||||
{"test_dict_iteration", test_dict_iteration, METH_NOARGS},
|
{"test_dict_iteration", test_dict_iteration, METH_NOARGS},
|
||||||
{"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS},
|
{"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS},
|
||||||
|
@ -3590,7 +3279,6 @@ static PyMethodDef TestMethods[] = {
|
||||||
METH_VARARGS | METH_KEYWORDS},
|
METH_VARARGS | METH_KEYWORDS},
|
||||||
{"get_crossinterp_data", get_crossinterp_data, METH_VARARGS},
|
{"get_crossinterp_data", get_crossinterp_data, METH_VARARGS},
|
||||||
{"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS},
|
{"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS},
|
||||||
{"with_tp_del", with_tp_del, METH_VARARGS},
|
|
||||||
{"create_cfunction", create_cfunction, METH_NOARGS},
|
{"create_cfunction", create_cfunction, METH_NOARGS},
|
||||||
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS,
|
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS,
|
||||||
PyDoc_STR("set_error_class(error_class) -> None")},
|
PyDoc_STR("set_error_class(error_class) -> None")},
|
||||||
|
@ -3641,7 +3329,6 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"meth_fastcall", _PyCFunction_CAST(meth_fastcall), METH_FASTCALL},
|
{"meth_fastcall", _PyCFunction_CAST(meth_fastcall), METH_FASTCALL},
|
||||||
{"meth_fastcall_keywords", _PyCFunction_CAST(meth_fastcall_keywords), METH_FASTCALL|METH_KEYWORDS},
|
{"meth_fastcall_keywords", _PyCFunction_CAST(meth_fastcall_keywords), METH_FASTCALL|METH_KEYWORDS},
|
||||||
{"pynumber_tobase", pynumber_tobase, METH_VARARGS},
|
{"pynumber_tobase", pynumber_tobase, METH_VARARGS},
|
||||||
{"without_gc", without_gc, METH_O},
|
|
||||||
{"test_set_type_size", test_set_type_size, METH_NOARGS},
|
{"test_set_type_size", test_set_type_size, METH_NOARGS},
|
||||||
{"test_py_clear", test_py_clear, METH_NOARGS},
|
{"test_py_clear", test_py_clear, METH_NOARGS},
|
||||||
{"test_py_setref", test_py_setref, METH_NOARGS},
|
{"test_py_setref", test_py_setref, METH_NOARGS},
|
||||||
|
@ -3675,8 +3362,6 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
|
{"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
|
||||||
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
|
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
|
||||||
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
|
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
|
||||||
{"test_gc_visit_objects_basic", test_gc_visit_objects_basic, METH_NOARGS, NULL},
|
|
||||||
{"test_gc_visit_objects_exit_early", test_gc_visit_objects_exit_early, METH_NOARGS, NULL},
|
|
||||||
{"test_atexit", test_atexit, METH_NOARGS},
|
{"test_atexit", test_atexit, METH_NOARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
@ -4223,17 +3908,6 @@ PyInit__testcapi(void)
|
||||||
Py_INCREF(&MethStatic_Type);
|
Py_INCREF(&MethStatic_Type);
|
||||||
PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type);
|
PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type);
|
||||||
|
|
||||||
PyObject *ObjExtraData_Type = PyType_FromModuleAndSpec(
|
|
||||||
m, &ObjExtraData_TypeSpec, NULL);
|
|
||||||
if (ObjExtraData_Type == 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
int ret = PyModule_AddType(m, (PyTypeObject*)ObjExtraData_Type);
|
|
||||||
Py_DECREF(ObjExtraData_Type);
|
|
||||||
if (ret < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX));
|
PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX));
|
||||||
PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN));
|
PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN));
|
||||||
PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX));
|
PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX));
|
||||||
|
@ -4327,6 +4001,9 @@ PyInit__testcapi(void)
|
||||||
if (_PyTestCapi_Init_Immortal(m) < 0) {
|
if (_PyTestCapi_Init_Immortal(m) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (_PyTestCapi_Init_GC(m) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef LIMITED_API_AVAILABLE
|
#ifndef LIMITED_API_AVAILABLE
|
||||||
PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False);
|
PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False);
|
||||||
|
|
|
@ -113,6 +113,7 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\buffer.c" />
|
<ClCompile Include="..\Modules\_testcapi\buffer.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\pyos.c" />
|
<ClCompile Include="..\Modules\_testcapi\pyos.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\gc.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="..\PC\python_nt.rc" />
|
<ResourceCompile Include="..\PC\python_nt.rc" />
|
||||||
|
|
|
@ -66,6 +66,9 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\pyos.c">
|
<ClCompile Include="..\Modules\_testcapi\pyos.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\gc.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="..\PC\python_nt.rc">
|
<ResourceCompile Include="..\PC\python_nt.rc">
|
||||||
|
|
Loading…
Reference in a new issue