static int g_switchstack(void) { /* perform a stack switch according to some global variables that must be set before: - ts_current: current greenlet (holds a reference) - ts_target: greenlet to switch to - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_target (holds a reference) - ts_passaround_kwargs: same as ts_passaround_args */ int err = slp_switch(); if (err < 0) { /* error */ } else { greenlet* target = ts_target; greenlet* origin = ts_current; ts_current = target; } return err; }
STATIC_NOINLINE void *_stacklet_switchstack(void *(*save_state)(void*, void*), void *(*restore_state)(void*, void*), void *extra) { return slp_switch(save_state, restore_state, extra); }
static int g_switchstack(void) { /* perform a stack switch according to some global variables that must be set before: - ts_current: current greenlet (holds a reference) - ts_target: greenlet to switch to - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_target (holds a reference) - ts_passaround_kwargs: same as ts_passaround_args */ int err; { /* save state */ PyGreenlet* current = ts_current; PyThreadState* tstate = PyThreadState_GET(); current->recursion_depth = tstate->recursion_depth; current->top_frame = tstate->frame; current->exc_type = tstate->exc_type; current->exc_value = tstate->exc_value; current->exc_traceback = tstate->exc_traceback; } err = slp_switch(); if (err < 0) { /* error */ PyGreenlet* current = ts_current; current->top_frame = NULL; current->exc_type = NULL; current->exc_value = NULL; current->exc_traceback = NULL; } else { PyObject* args = ts_passaround_args; PyObject* kwargs = ts_passaround_kwargs; PyGreenlet* target = ts_target; PyGreenlet* origin = ts_current; PyThreadState* tstate = PyThreadState_GET(); tstate->recursion_depth = target->recursion_depth; tstate->frame = target->top_frame; target->top_frame = NULL; tstate->exc_type = target->exc_type; target->exc_type = NULL; tstate->exc_value = target->exc_value; target->exc_value = NULL; tstate->exc_traceback = target->exc_traceback; target->exc_traceback = NULL; ts_current = target; Py_INCREF(target); Py_DECREF(origin); ts_passaround_args = args; ts_passaround_kwargs = kwargs; } return err; }
int slp_transfer(PyCStackObject **cstprev, PyCStackObject *cst, PyTaskletObject *prev) { PyThreadState *ts = PyThreadState_GET(); /* since we change the stack we must assure that the protocol was met */ STACKLESS_ASSERT(); if ((intptr_t *) &ts > ts->st.cstack_base) return climb_stack_and_transfer(cstprev, cst, prev); if (cst == NULL || cst->ob_size == 0) cst = ts->st.initial_stub; if (cst != NULL) { if (cst->tstate != ts) { PyErr_SetString(PyExc_SystemError, "bad thread state in transfer"); return -1; } if (ts->st.cstack_base != cst->startaddr) { PyErr_SetString(PyExc_SystemError, "bad stack reference in transfer"); return -1; } /* record the context of the target stack */ ts->st.serial_last_jump = cst->serial; /* * if stacks are same and refcount==1, it must be the same * task. In this case, we would destroy the target before * switching. Therefore, we simply don't switch, just save. */ if (cstprev && *cstprev == cst && cst->ob_refcnt == 1) cst = NULL; } _cstprev = cstprev; _cst = cst; _prev = prev; return slp_switch(); }
static int g_switchstack(void) { /* Perform a stack switch according to some global variables that must be set before: - ts_current: current greenlet (holds a reference) - ts_target: greenlet to switch to (weak reference) - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_target (holds a reference) - ts_passaround_kwargs: switch kwargs (holds a reference) On return results are passed via global variables as well: - ts_origin: originating greenlet (holds a reference) - ts_current: current greenlet (holds a reference) - ts_passaround_args: NULL if PyErr_Occurred(), else a tuple of args sent to ts_current (holds a reference) - ts_passaround_kwargs: switch kwargs (holds a reference) It is very important that stack switch is 'atomic', i.e. no calls into other Python code allowed (except very few that are safe), because global variables are very fragile. */ int err; { /* save state */ PyGreenlet* current = ts_current; PyThreadState* tstate = PyThreadState_GET(); current->recursion_depth = tstate->recursion_depth; current->top_frame = tstate->frame; current->exc_type = tstate->exc_type; current->exc_value = tstate->exc_value; current->exc_traceback = tstate->exc_traceback; } err = slp_switch(); if (err < 0) { /* error */ PyGreenlet* current = ts_current; current->top_frame = NULL; current->exc_type = NULL; current->exc_value = NULL; current->exc_traceback = NULL; assert(ts_origin == NULL); ts_target = NULL; } else { PyGreenlet* target = ts_target; PyGreenlet* origin = ts_current; PyThreadState* tstate = PyThreadState_GET(); tstate->recursion_depth = target->recursion_depth; tstate->frame = target->top_frame; target->top_frame = NULL; tstate->exc_type = target->exc_type; target->exc_type = NULL; tstate->exc_value = target->exc_value; target->exc_value = NULL; tstate->exc_traceback = target->exc_traceback; target->exc_traceback = NULL; assert(ts_origin == NULL); Py_INCREF(target); ts_current = target; ts_origin = origin; ts_target = NULL; } return err; }