mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #14432: Remove the thread state field from the frame structure. Fix a
crash when a generator is created in a C thread that is destroyed while the generator is still used. The issue was that a generator contains a frame, and the frame kept a reference to the Python state of the destroyed C thread. The crash occurs when a trace function is setup.
This commit is contained in:
parent
62ca10051b
commit
fdeb6ec45a
8 changed files with 179 additions and 45 deletions
|
@ -2869,6 +2869,93 @@ PyDoc_STRVAR(docstring_with_signature_and_extra_newlines,
|
|||
"This docstring has a valid signature and some extra newlines."
|
||||
);
|
||||
|
||||
typedef struct {
|
||||
PyThread_type_lock start_event;
|
||||
PyThread_type_lock exit_event;
|
||||
PyObject *callback;
|
||||
} test_c_thread_t;
|
||||
|
||||
static void
|
||||
temporary_c_thread(void *data)
|
||||
{
|
||||
test_c_thread_t *test_c_thread = data;
|
||||
PyGILState_STATE state;
|
||||
PyObject *res;
|
||||
|
||||
PyThread_release_lock(test_c_thread->start_event);
|
||||
|
||||
/* Allocate a Python thread state for this thread */
|
||||
state = PyGILState_Ensure();
|
||||
|
||||
res = PyObject_CallFunction(test_c_thread->callback, "", NULL);
|
||||
Py_CLEAR(test_c_thread->callback);
|
||||
|
||||
if (res == NULL) {
|
||||
PyErr_Print();
|
||||
}
|
||||
else {
|
||||
Py_DECREF(res);
|
||||
}
|
||||
|
||||
/* Destroy the Python thread state for this thread */
|
||||
PyGILState_Release(state);
|
||||
|
||||
PyThread_release_lock(test_c_thread->exit_event);
|
||||
|
||||
PyThread_exit_thread();
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
call_in_temporary_c_thread(PyObject *self, PyObject *callback)
|
||||
{
|
||||
PyObject *res = NULL;
|
||||
test_c_thread_t test_c_thread;
|
||||
long thread;
|
||||
|
||||
PyEval_InitThreads();
|
||||
|
||||
test_c_thread.start_event = PyThread_allocate_lock();
|
||||
test_c_thread.exit_event = PyThread_allocate_lock();
|
||||
test_c_thread.callback = NULL;
|
||||
if (!test_c_thread.start_event || !test_c_thread.exit_event) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "could not allocate lock");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Py_INCREF(callback);
|
||||
test_c_thread.callback = callback;
|
||||
|
||||
PyThread_acquire_lock(test_c_thread.start_event, 1);
|
||||
PyThread_acquire_lock(test_c_thread.exit_event, 1);
|
||||
|
||||
thread = PyThread_start_new_thread(temporary_c_thread, &test_c_thread);
|
||||
if (thread == -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "unable to start the thread");
|
||||
PyThread_release_lock(test_c_thread.start_event);
|
||||
PyThread_release_lock(test_c_thread.exit_event);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
PyThread_acquire_lock(test_c_thread.start_event, 1);
|
||||
PyThread_release_lock(test_c_thread.start_event);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
PyThread_acquire_lock(test_c_thread.exit_event, 1);
|
||||
PyThread_release_lock(test_c_thread.exit_event);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
res = Py_None;
|
||||
|
||||
exit:
|
||||
Py_CLEAR(test_c_thread.callback);
|
||||
if (test_c_thread.start_event)
|
||||
PyThread_free_lock(test_c_thread.start_event);
|
||||
if (test_c_thread.exit_event)
|
||||
PyThread_free_lock(test_c_thread.exit_event);
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyMethodDef TestMethods[] = {
|
||||
{"raise_exception", raise_exception, METH_VARARGS},
|
||||
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
|
||||
|
@ -2997,6 +3084,8 @@ static PyMethodDef TestMethods[] = {
|
|||
{"docstring_with_signature_and_extra_newlines",
|
||||
(PyCFunction)test_with_docstring, METH_NOARGS,
|
||||
docstring_with_signature_and_extra_newlines},
|
||||
{"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O,
|
||||
PyDoc_STR("set_error_class(error_class) -> None")},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue