summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2003-04-05 17:35:54 (GMT)
committerTim Peters <tim.peters@gmail.com>2003-04-05 17:35:54 (GMT)
commit86b993b6cfffa6fee144054d30fd94d02a3717cb (patch)
tree73d09eae152995dede57ef56aa4eb84e830a440c /Modules
parent93ad66dea9814b6ad59710171227d84c78bbb4b9 (diff)
downloadcpython-86b993b6cfffa6fee144054d30fd94d02a3717cb.zip
cpython-86b993b6cfffa6fee144054d30fd94d02a3717cb.tar.gz
cpython-86b993b6cfffa6fee144054d30fd94d02a3717cb.tar.bz2
New comments. Rewrote has_finalizer() as a sequence of ifs instead of
squashed-together conditional operators; makes it much easier to step thru in the debugger, and to set a breakpoint on the only dangerous path.
Diffstat (limited to 'Modules')
-rw-r--r--Modules/gcmodule.c27
1 files changed, 21 insertions, 6 deletions
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 10a90b1..729e9d5 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -339,13 +339,26 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
}
}
-/* return true if object has a finalization method */
+/* Return true if object has a finalization method.
+ * CAUTION: An instance of an old-style class has to be checked for a
+ *__del__ method, and that can cause arbitrary Python code to get executed
+ * via the class's __getattr__ hook (if any). This function can therefore
+ * mutate the object graph, and that's been the source of subtle bugs.
+ */
static int
has_finalizer(PyObject *op)
{
- return PyInstance_Check(op) ? PyObject_HasAttr(op, delstr) :
- PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE) ?
- op->ob_type->tp_del != NULL : 0;
+ if (PyInstance_Check(op)) {
+ /* This is the dangerous path: hasattr can invoke
+ * the class __getattr__(), and that can do anything.
+ */
+ assert(delstr != NULL);
+ return PyObject_HasAttr(op, delstr);
+ }
+ else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE))
+ return op->ob_type->tp_del != NULL;
+ else
+ return 0;
}
/* Move all objects out of unreachable and into collectable or finalizers.
@@ -586,8 +599,10 @@ collect(int generation)
* instance objects with __del__ methods.
*
* Move each object into the collectable set or the finalizers set.
- * It's possible that a classic class with a getattr() hook will
- * be revived or deallocated in this step.
+ * Because we need to check for __del__ methods on instances of
+ * classic classes, arbitrary Python code may get executed by
+ * getattr hooks: that may resurrect or deallocate (via refcount
+ * falling to 0) unreachable objects, so this is very delicate.
*/
gc_list_init(&collectable);
gc_list_init(&finalizers);