Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 39 additions & 25 deletions context/_coroutine.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,23 @@ static void *coroutine_wrapper(void *action_, void *arg_)
PyThreadState *thread_state = PyThreadState_GET();

/* New coroutine gets a brand new Python interpreter stack frame. */
#if PY_VERSION_HEX >= 0x30B0000
PyThreadState *new_threadstate = PyThreadState_New(thread_state->interp);
thread_state = PyThreadState_Swap(new_threadstate);
#else
thread_state->frame = NULL;
thread_state->recursion_depth = 0;

/* Also reset the exception state in case it's non NULL at this point. We
* don't own these pointers at this point, coroutine_switch does. */
#if PY_VERSION_HEX >= 0x03070000
/* In Python 3.7 the exec info moved. */
thread_state->exc_state = (_PyErr_StackItem) { };
thread_state->exc_info = &thread_state->exc_state;
#else
thread_state->exc_type = NULL;
thread_state->exc_value = NULL;
thread_state->exc_traceback = NULL;
#if PY_VERSION_HEX >= 0x03070000
/* Also reset the exception state in case it's non NULL at this point. We
* don't own these pointers at this point, coroutine_switch does. */
/* In Python 3.7 the exec info moved. */
thread_state->exc_state = (_PyErr_StackItem) { };
thread_state->exc_info = &thread_state->exc_state;
#else
thread_state->exc_type = NULL;
thread_state->exc_value = NULL;
thread_state->exc_traceback = NULL;
#endif
#endif

/* Call the given action with the passed argument. */
Expand All @@ -102,20 +106,24 @@ static void *coroutine_wrapper(void *action_, void *arg_)
Py_DECREF(action);
Py_DECREF(arg);


#if PY_VERSION_HEX < 0x30B0000
/* Some of the stuff we've initialised can leak through, so far I've only
* seen exc_type still set at this point, but maybe other fields can also
* leak. Avoid a memory leak by making sure we're not holding onto these.
* All these pointers really are defunct, because as soon as we return
* coroutine_switch will replace all these values. */
Py_XDECREF(thread_state->frame);
#if PY_VERSION_HEX >= 0x03070000
Py_XDECREF(thread_state->exc_state.exc_type);
Py_XDECREF(thread_state->exc_state.exc_value);
Py_XDECREF(thread_state->exc_state.exc_traceback);
#else
Py_XDECREF(thread_state->exc_type);
Py_XDECREF(thread_state->exc_value);
Py_XDECREF(thread_state->exc_traceback);

#if PY_VERSION_HEX >= 0x03070000
Py_XDECREF(thread_state->exc_state.exc_type);
Py_XDECREF(thread_state->exc_state.exc_value);
Py_XDECREF(thread_state->exc_state.exc_traceback);
#else
Py_XDECREF(thread_state->exc_type);
Py_XDECREF(thread_state->exc_value);
Py_XDECREF(thread_state->exc_traceback);
#endif
#endif

return result;
Expand Down Expand Up @@ -148,25 +156,28 @@ static PyObject *coroutine_switch(PyObject *Self, PyObject *args)
PyObject *arg;
if (PyArg_ParseTuple(args, "O&O", get_cocore, &target, &arg))
{
PyThreadState *thread_state = PyThreadState_GET();

#if PY_VERSION_HEX < 0x30B0000
/* Need to switch the Python interpreter's record of recursion depth and
* top frame around as we switch frames, otherwise the interpreter gets
* confused and thinks we've recursed too deep. In truth tracking this
* stuff is the only reason this code is in a Python extension! */
PyThreadState *thread_state = PyThreadState_GET();
struct _frame *python_frame = thread_state->frame;
int recursion_depth = thread_state->recursion_depth;

/* We also need to switch the exception state around: if we don't do
* this then we get confusion about the lifetime of exception state
* between coroutines. The most obvious problem is that the exception
* isn't properly cleared on function return. */
#if PY_VERSION_HEX >= 0x03070000
#if PY_VERSION_HEX >= 0x03070000
_PyErr_StackItem exc_state = thread_state->exc_state;
_PyErr_StackItem *exc_info = thread_state->exc_info;
#else
#else
PyObject *exc_type = thread_state->exc_type;
PyObject *exc_value = thread_state->exc_value;
PyObject *exc_traceback = thread_state->exc_traceback;
#endif
#endif

/* Switch to new coroutine. For the duration arg needs an extra
Expand All @@ -175,20 +186,23 @@ static PyObject *coroutine_switch(PyObject *Self, PyObject *args)
Py_INCREF(arg);
PyObject *result = switch_cocore(target, arg);

#if PY_VERSION_HEX >= 0x30B0000
PyThreadState_Swap(thread_state);
#else
/* Restore previously saved state. I wonder if PyThreadState_GET()
* really needs to be called again here... */
thread_state = PyThreadState_GET();
thread_state->frame = python_frame;
thread_state->recursion_depth = recursion_depth;

/* Restore the exception state. */
#if PY_VERSION_HEX >= 0x03070000
#if PY_VERSION_HEX >= 0x03070000
thread_state->exc_state = exc_state;
thread_state->exc_info = exc_info;
#else
#else
thread_state->exc_type = exc_type;
thread_state->exc_value = exc_value;
thread_state->exc_traceback = exc_traceback;
#endif
#endif
return result;
}
Expand Down
2 changes: 1 addition & 1 deletion cothread/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.18.2'
__version__ = '2.19.0'