From 33b8f81aa479bbd176748cfea2541a1cfc794b48 Mon Sep 17 00:00:00 2001 From: Anselm Kruis Date: Fri, 9 Jul 2021 14:40:51 +0200 Subject: [PATCH] Stackless issue #282: Refactor slp_eval_frame Decouple the cleanup logic for initial stubs of sub-interpreters from the stack handling. Previously this logic depended on the value of SLP_CSTACK_GOODGAP being positive. --- Stackless/core/stacklesseval.c | 86 +++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 695c7defcf22a0..93d8daa29dd6c3 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -317,6 +317,7 @@ slp_eval_frame(PyFrameObject *f) PyThreadState *ts = _PyThreadState_GET(); PyFrameObject *fprev = f->f_back; intptr_t * stackref; + int set_cstack_base; PyObject *retval; if (fprev == NULL && ts->st.main == NULL) { @@ -345,51 +346,58 @@ slp_eval_frame(PyFrameObject *f) /* mark the stack base */ stackref = SLP_STACK_REFPLUS + (intptr_t *) &f; - if (ts->st.cstack_base == NULL) + set_cstack_base = ts->st.cstack_base == NULL; + if (set_cstack_base) ts->st.cstack_base = stackref - SLP_CSTACK_GOODGAP; if (stackref > ts->st.cstack_base) { - PyCStackObject *initial_stub; - retval = climb_stack_and_eval_frame(f); - initial_stub = ts->st.initial_stub; - /* cst might be NULL in OOM conditions */ - if (ts->interp != _PyRuntime.interpreters.main && initial_stub != NULL) { - PyCStackObject *cst; - register int found = 0; - assert(initial_stub->startaddr == ts->st.cstack_base); - for (cst = initial_stub->next; cst != initial_stub; cst = cst->next) { - if (Py_SIZE(cst) != 0 && cst->task != NULL && - cst->tstate == ts) { - assert(cst->startaddr == ts->st.cstack_base); - found = 1; - break; - } - } - if (!found) { - Py_CLEAR(ts->st.initial_stub); - ts->st.cstack_base = NULL; - ts->st.cstack_root = NULL; - } + retval = climb_stack_and_eval_frame(f); /* recursively calls slp_eval_frame(f) */ + } else { + assert(SLP_CURRENT_FRAME(ts) == NULL); /* else we would change the current frame */ + SLP_STORE_NEXT_FRAME(ts, f); + returning = make_initial_stub(); + if (returning < 0) + return NULL; + /* returning will be 1 if we "switched back" to this stub, and 0 + * if this is the original call that just created the stub. + * If the stub is being reused, the argument, i.e. the frame, + * is in ts->frame + */ + SLP_ASSERT_FRAME_IN_TRANSFER(ts); + if (returning != 1) { + assert(f == SLP_PEEK_NEXT_FRAME(ts)); } - return retval; + assert(ts->interp->st.initial_tstate != NULL); + assert(ts->interp->st.mem_bomb != NULL); + + retval = slp_run_tasklet(); } - assert(SLP_CURRENT_FRAME(ts) == NULL); /* else we would change the current frame */ - SLP_STORE_NEXT_FRAME(ts, f); - returning = make_initial_stub(); - if (returning < 0) - return NULL; - /* returning will be 1 if we "switched back" to this stub, and 0 - * if this is the original call that just created the stub. - * If the stub is being reused, the argument, i.e. the frame, - * is in ts->frame - */ - SLP_ASSERT_FRAME_IN_TRANSFER(ts); - if (returning != 1) { - assert(f == SLP_PEEK_NEXT_FRAME(ts)); + /* Clear the initial stub, except for the main interpreter. + * Without this code, terminating sub-interpreters would leak references. */ + if (set_cstack_base /* only for the outermost call */ + && ts->interp != _PyRuntime.interpreters.main /* not for the main interpreter */ + && ts->st.initial_stub != NULL) /* not yet cleared */ + { + PyCStackObject *initial_stub = ts->st.initial_stub; + PyCStackObject *cst; + register int found = 0; + assert(initial_stub->startaddr == ts->st.cstack_base); + for (cst = initial_stub->next; cst != initial_stub; cst = cst->next) { + if (Py_SIZE(cst) != 0 && cst->task != NULL && + cst->tstate == ts) { + assert(cst->startaddr == ts->st.cstack_base); + found = 1; /* Initial stub is still in use. */ + break; + } + } + if (!found) { + /* This initial stub is no longer in use, free it */ + Py_CLEAR(ts->st.initial_stub); + ts->st.cstack_base = NULL; + ts->st.cstack_root = NULL; + } } - assert(ts->interp->st.initial_tstate != NULL); - assert(ts->interp->st.mem_bomb != NULL); - return slp_run_tasklet(); + return retval; } assert(ts->interp->st.initial_tstate != NULL); assert(ts->interp->st.mem_bomb != NULL); /* see above */