mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 02:15:10 +00:00 
			
		
		
		
	 fda6bd842a
			
		
	
	
		fda6bd842a
		
			
		
	
	
	
	
		
			
			PyObject_Del() is just a alias to PyObject_Free() kept for backward compatibility. Use directly PyObject_Free() instead.
		
			
				
	
	
		
			343 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #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_Free;
 | |
|         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_LookupRef(Py_TYPE(self), tp_del);
 | |
|     Py_DECREF(tp_del);
 | |
|     if (del != NULL) {
 | |
|         res = PyObject_CallOneArg(del, self);
 | |
|         Py_DECREF(del);
 | |
|         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_ResurrectReference(self);
 | |
|     }
 | |
|     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;
 | |
| }
 |