From b43b156c5e8909315ece92fc434123d3f93b5a61 Mon Sep 17 00:00:00 2001
From: Stefan Behnel <stefan_ml@behnel.de>
Date: Fri, 25 Aug 2017 18:22:41 +0200
Subject: [PATCH] Only reset the frame backlink of *internally* created
 exception tracebacks in generators/coroutines, not the ones stored away from
 the caller state.

---
 Cython/Utility/Coroutine.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c
index a179e5793..6f9c6af36 100644
--- a/Cython/Utility/Coroutine.c
+++ b/Cython/Utility/Coroutine.c
@@ -397,23 +397,18 @@ static PyObject *__Pyx_Coroutine_Throw(PyObject *gen, PyObject *args); /*proto*/
     }
 #define __Pyx_Coroutine_ResetAndClearException(self) { \
     __Pyx_ExceptionReset((self)->exc_type, (self)->exc_value, (self)->exc_traceback); \
-    __Pyx_Coroutine_ResetFrameBackpointer(self); \
     (self)->exc_type = (self)->exc_value = (self)->exc_traceback = NULL; \
     }
 
 #if CYTHON_FAST_THREAD_STATE
 #define __Pyx_PyGen_FetchStopIterationValue(pvalue) \
     __Pyx_PyGen__FetchStopIterationValue($local_tstate_cname, pvalue)
-#define __Pyx_Coroutine_ResetFrameBackpointer(self) \
-    __Pyx__Coroutine_ResetFrameBackpointer($local_tstate_cname, self)
 #else
 #define __Pyx_PyGen_FetchStopIterationValue(pvalue) \
     __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, pvalue)
-#define __Pyx_Coroutine_ResetFrameBackpointer(self) \
-    __Pyx__Coroutine_ResetFrameBackpointer(__Pyx_PyThreadState_Current, self)
 #endif
 static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *tstate, PyObject **pvalue); /*proto*/
-static CYTHON_INLINE void __Pyx__Coroutine_ResetFrameBackpointer(PyThreadState *tstate, __pyx_CoroutineObject *self);
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__pyx_CoroutineObject *self); /*proto*/
 
 
 //////////////////// Coroutine.proto ////////////////////
@@ -667,7 +662,14 @@ PyObject *__Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value, i
     tstate = __Pyx_PyThreadState_Current;
 #endif
 
-    if (self->exc_type && self->exc_type != Py_None) {
+    // Traceback/Frame rules:
+    // - on entry, save external exception state in self->exc_*, restore it on exit
+    // - on exit, keep internally generated exceptions in self->exc_*, clear everything else
+    // - on entry, set "f_back" pointer of internal exception traceback to (current) outer call frame
+    // - on exit, clear "f_back" of internal exception traceback
+    // - do not touch external frames and tracebacks
+
+    if (self->exc_type) {
 #if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_PYSTON
         // FIXME: what to do in PyPy?
 #else
@@ -700,20 +702,17 @@ PyObject *__Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value, i
     return retval;
 }
 
-static CYTHON_INLINE void __Pyx__Coroutine_ResetFrameBackpointer(PyThreadState *tstate, __pyx_CoroutineObject *self) {
+static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__pyx_CoroutineObject *self) {
     // Don't keep the reference to f_back any longer than necessary.  It
     // may keep a chain of frames alive or it could create a reference
     // cycle.
-    if (self->exc_traceback) {
+    if (likely(self->exc_traceback)) {
 #if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_PYSTON
     // FIXME: what to do in PyPy?
 #else
         PyTracebackObject *tb = (PyTracebackObject *) self->exc_traceback;
         PyFrameObject *f = tb->tb_frame;
-        // do not accidentally break any other frame links
-        // FIXME: any other cases? e.g. do we need to follow up the back link chain?
-        if (f->f_back == tstate->frame)
-            Py_CLEAR(f->f_back);
+        Py_CLEAR(f->f_back);
 #endif
     }
 }
-- 
2.30.9