Add has_finalizer predictate function. Use it when deciding which

objects to save in gc.garbage.  This should be the last change needed to
fix SF bug 477059: "__del__ on new classes vs. GC".

Note that this change slightly changes the behavior of the collector.
Before, if a cycle was found that contained instances with __del__
methods then all instance objects in that cycle were saved in
gc.garbage.  Now, only objects with __del__ methods are saved in
gc.garbage.
This commit is contained in:
Neil Schemenauer 2001-11-01 17:35:23 +00:00
parent f49c6f9954
commit a765c120f6
1 changed files with 26 additions and 14 deletions

View File

@ -226,24 +226,36 @@ move_root_reachable(PyGC_Head *reachable)
} }
} }
/* Move all objects with finalizers (instances with __del__) */ /* return true of object has a finalization method */
static void static int
move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) has_finalizer(PyObject *op)
{ {
PyGC_Head *next;
PyGC_Head *gc = unreachable->gc.gc_next;
static PyObject *delstr = NULL; static PyObject *delstr = NULL;
if (delstr == NULL) { if (delstr == NULL) {
delstr = PyString_InternFromString("__del__"); delstr = PyString_InternFromString("__del__");
if (delstr == NULL) if (delstr == NULL)
Py_FatalError("PyGC: can't initialize __del__ string"); Py_FatalError("PyGC: can't initialize __del__ string");
} }
if ((PyInstance_Check(op) ||
PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) &&
PyObject_HasAttr(op, delstr)) {
return 1;
}
else {
return 0;
}
}
/* Move all objects with finalizers (instances with __del__) */
static void
move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
{
PyGC_Head *next;
PyGC_Head *gc = unreachable->gc.gc_next;
for (; gc != unreachable; gc=next) { for (; gc != unreachable; gc=next) {
PyObject *op = FROM_GC(gc); PyObject *op = FROM_GC(gc);
next = gc->gc.gc_next; next = gc->gc.gc_next;
if ((PyInstance_Check(op) || if (has_finalizer(op)) {
PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) &&
PyObject_HasAttr(op, delstr)) {
gc_list_remove(gc); gc_list_remove(gc);
gc_list_append(gc, finalizers); gc_list_append(gc, finalizers);
} }
@ -269,7 +281,7 @@ static void
debug_instance(char *msg, PyInstanceObject *inst) debug_instance(char *msg, PyInstanceObject *inst)
{ {
char *cname; char *cname;
/* be careful not to create new dictionaries */ /* simple version of instance_repr */
PyObject *classname = inst->in_class->cl_name; PyObject *classname = inst->in_class->cl_name;
if (classname != NULL && PyString_Check(classname)) if (classname != NULL && PyString_Check(classname))
cname = PyString_AsString(classname); cname = PyString_AsString(classname);
@ -302,11 +314,11 @@ handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
for (gc = finalizers->gc.gc_next; gc != finalizers; for (gc = finalizers->gc.gc_next; gc != finalizers;
gc = finalizers->gc.gc_next) { gc = finalizers->gc.gc_next) {
PyObject *op = FROM_GC(gc); PyObject *op = FROM_GC(gc);
if ((debug & DEBUG_SAVEALL) || PyInstance_Check(op)) { if ((debug & DEBUG_SAVEALL) || has_finalizer(op)) {
/* If SAVEALL is not set then just append /* If SAVEALL is not set then just append objects with
* instances to the list of garbage. We assume * finalizers to the list of garbage. All objects in
* that all objects in the finalizers list are * the finalizers list are reachable from those
* reachable from instances. */ * objects. */
PyList_Append(garbage, op); PyList_Append(garbage, op);
} }
/* object is now reachable again */ /* object is now reachable again */