From 2ba96610bfeda03381dd411d5694fae311159a0c Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Mon, 10 Apr 2006 17:51:05 +0000 Subject: SF Patch #1463867: Improved generator finalization to allow generators that are suspended outside of any try/except/finally blocks to be garbage collected even if they are part of a cycle. Generators that suspend inside of an active try/except or try/finally block (including those created by a ``with`` statement) are still not GC-able if they are part of a cycle, however. --- Include/genobject.h | 1 + Modules/gcmodule.c | 6 +++++- Objects/genobject.c | 20 ++++++++++++++++++++ Python/ceval.c | 3 +++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Include/genobject.h b/Include/genobject.h index f4226ed..1ecd7ad 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -28,6 +28,7 @@ PyAPI_DATA(PyTypeObject) PyGen_Type; #define PyGen_CheckExact(op) ((op)->ob_type == &PyGen_Type) PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); +PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); #ifdef __cplusplus } diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index a8976b3..5bf95b9 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -413,8 +413,12 @@ has_finalizer(PyObject *op) assert(delstr != NULL); return _PyInstance_Lookup(op, delstr) != NULL; } - else + else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) return op->ob_type->tp_del != NULL; + else if (PyGen_CheckExact(op)) + return PyGen_NeedsFinalizing((PyGenObject *)op); + else + return 0; } /* Move the objects in unreachable with __del__ methods into `finalizers`. diff --git a/Objects/genobject.c b/Objects/genobject.c index e7b8f87..a3eae6a 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -5,6 +5,7 @@ #include "genobject.h" #include "ceval.h" #include "structmember.h" +#include "opcode.h" static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) @@ -358,3 +359,22 @@ PyGen_New(PyFrameObject *f) _PyObject_GC_TRACK(gen); return (PyObject *)gen; } + +int +PyGen_NeedsFinalizing(PyGenObject *gen) +{ + int i; + PyFrameObject *f = gen->gi_frame; + + if ((PyObject *)f == Py_None || f->f_stacktop==NULL || f->f_iblock<=0) + return 0; /* no frame or no blockstack == no finalization */ + + for (i=f->f_iblock; i>=0; i--) { + if (f->f_blockstack[i].b_type != SETUP_LOOP) + /* any block type besides a loop requires cleanup */ + return 1; + } + + /* No blocks except loops, it's safe to skip finalization */ + return 0; +} diff --git a/Python/ceval.c b/Python/ceval.c index cc1eb97..6302ede 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2179,6 +2179,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) case SETUP_LOOP: case SETUP_EXCEPT: case SETUP_FINALLY: + /* NOTE: If you add any new block-setup opcodes that are not try/except/finally + handlers, you may need to update the PyGen_NeedsFinalizing() function. */ + PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, STACK_LEVEL()); continue; -- cgit v0.12