diff --git a/Include/objimpl.h b/Include/objimpl.h --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -231,12 +231,31 @@ /* C equivalent of gc.collect(). */ PyAPI_FUNC(Py_ssize_t) PyGC_Collect(void); +/* return flags value of the tp_is_gc slot function. This function + * can be called to enquire about the state of an instance, if it + * was allocated with a GC header, or during garbage collection, + * if is safe or unsafe to collect it. + * + * PyGC_CAN_COLLECT and PyGC_CANNOT_COLLECT are mutually exclusive. + * The former means that even if the object has a tp_del slot + * (a finalizer), in the current state the finalizer is not neded, + * and even if called will do nothing but call Py_DECREF on members. + * PyGC_CANNOT_COLLECT means the opposite: Even if there is no + * finalizer, it is not safe to collect this object because doing + * so may have dangerous side effects similar to running a + * finalizer. + */ +#define PyGC_HAS_GC 1 /* instance has a GC head */ +#define PyGC_CAN_COLLECT 2 /* instance _can_ be collected */ +#define PyGC_CANNOT_COLLECT 4 /* instance _cannot_ be collected */ + /* Test if a type has a GC head */ #define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) /* Test if an object has a GC head */ #define PyObject_IS_GC(o) (PyType_IS_GC(Py_TYPE(o)) && \ - (Py_TYPE(o)->tp_is_gc == NULL || Py_TYPE(o)->tp_is_gc(o))) + (Py_TYPE(o)->tp_is_gc == NULL || \ + (Py_TYPE(o)->tp_is_gc(o) & PyGC_HAS_GC))) PyAPI_FUNC(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, Py_ssize_t); #define PyObject_GC_Resize(type, op, n) \ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -457,14 +457,24 @@ } } -/* Return true if object has a finalization method. */ +/* Return true if object has a finalization method, or signals + * itself as not being collectable at this time + */ static int has_finalizer(PyObject *op) { - if (PyGen_CheckExact(op)) - return PyGen_NeedsFinalizing((PyGenObject *)op); - else - return op->ob_type->tp_del != NULL; + if (Py_TYPE(op)->tp_is_gc != NULL) { + /* the flags returned by tp_is_gc override the simple + * method of detecting a finalizer, based on the + * current state of the instance + */ + int flag = Py_TYPE(op)->tp_is_gc(op); + if (flag & PyGC_CANNOT_COLLECT) + return 1; /* even if no tp_del */ + if (flag & PyGC_CAN_COLLECT) + return 0; /* even if there is a tp_del */ + } + return op->ob_type->tp_del != NULL; } /* Move the objects in unreachable with __del__ methods into `finalizers`. diff --git a/Objects/genobject.c b/Objects/genobject.c --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -469,6 +469,24 @@ return name; } +static int +gen_is_gc(PyGenObject *gen) +{ + int i, result = PyGC_HAS_GC; + PyFrameObject *f = gen->gi_frame; + + /* no frame or empty blockstack == no finalization */ + if (f == NULL || f->f_stacktop == NULL) + return result | PyGC_CAN_COLLECT; + + /* Any block type besides a loop requires cleanup. */ + for (i = 0; i < f->f_iblock; i++) + if (f->f_blockstack[i].b_type != SETUP_LOOP) + return result | PyGC_CANNOT_COLLECT; + + /* No blocks except loops, it's safe to skip finalization. */ + return result | PyGC_CAN_COLLECT; +} PyDoc_STRVAR(gen__name__doc__, "Return the name of the generator's associated code object."); @@ -535,7 +553,7 @@ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ - 0, /* tp_is_gc */ + (inquiry)gen_is_gc, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ @@ -564,17 +582,5 @@ int PyGen_NeedsFinalizing(PyGenObject *gen) { - int i; - PyFrameObject *f = gen->gi_frame; - - if (f == NULL || f->f_stacktop == NULL) - return 0; /* no frame or empty blockstack == no finalization */ - - /* Any block type besides a loop requires cleanup. */ - for (i = 0; i < f->f_iblock; i++) - if (f->f_blockstack[i].b_type != SETUP_LOOP) - return 1; - - /* No blocks except loops, it's safe to skip finalization. */ - return 0; + return (gen_is_gc(gen) & PyGC_CANNOT_COLLECT) ? 1 : 0; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2811,7 +2811,7 @@ static int type_is_gc(PyTypeObject *type) { - return type->tp_flags & Py_TPFLAGS_HEAPTYPE; + return (type->tp_flags & Py_TPFLAGS_HEAPTYPE) ? PyGC_HAS_GC : 0; } PyTypeObject PyType_Type = {