Index: Include/objimpl.h =================================================================== --- Include/objimpl.h (revision 82435) +++ Include/objimpl.h (working copy) @@ -306,6 +306,7 @@ PyAPI_FUNC(void) PyObject_GC_Track(void *); PyAPI_FUNC(void) PyObject_GC_UnTrack(void *); PyAPI_FUNC(void) PyObject_GC_Del(void *); +PyAPI_FUNC(void) PyObject_GC_Collectable(PyObject *, visitproc, void*, int); #define PyObject_GC_New(type, typeobj) \ ( (type *) _PyObject_GC_New(typeobj) ) Index: Modules/gcmodule.c =================================================================== --- Modules/gcmodule.c (revision 82435) +++ Modules/gcmodule.c (working copy) @@ -457,14 +457,52 @@ } } +/* A traversal callback for has_finalisers. It is a dummy, used to identify + * this phase of finalizer discovery by its function pointer, and thus + * enable the action of PyObject_GC_Collectable() below. + */ +static int +visit_has_finalizer(PyObject *op, void *data) +{ + assert(op != NULL); + return 0; +} + +/* An object can call this function from its traversal function + * to indicate whether it wishes to be collected or not + * this is useful for objects that have state that determines + * whether non-trivial actions need to be undertaken when + * it is deleted. + */ +PyAPI_FUNC(void) +PyObject_GC_Collectable(PyObject *op, visitproc proc, void *arg, + int can_collect) +{ + /* only do this during the move_finalizers phase */ + if (proc == &visit_has_finalizer) + *(int*)arg = can_collect; +} + /* Return true if object has a finalization method. */ static int has_finalizer(PyObject *op) { + traverseproc traverse; + int collectable; + if (PyGen_CheckExact(op)) return PyGen_NeedsFinalizing((PyGenObject *)op); - else - return op->ob_type->tp_del != NULL; + + /* Allow a dynamic decision per object */ + traverse = Py_TYPE(op)->tp_traverse; + collectable = -1; + (void) traverse(op, + (visitproc)visit_has_finalizer, + (void *)&collectable); + if (collectable >= 0) + return collectable == 0; + + return op->ob_type->tp_del != NULL; } /* Move the objects in unreachable with __del__ methods into `finalizers`.