diff options
author | Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> | 2023-01-11 10:33:31 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-11 10:33:31 (GMT) |
commit | 762745a124cbc297cf2fe6f3ec9ca1840bb2e873 (patch) | |
tree | afda022491baf91db77ddd5909833e91cb166aee /Modules | |
parent | 847d7708ba8739a5d5d31f22d71497527a7d8241 (diff) | |
download | cpython-762745a124cbc297cf2fe6f3ec9ca1840bb2e873.zip cpython-762745a124cbc297cf2fe6f3ec9ca1840bb2e873.tar.gz cpython-762745a124cbc297cf2fe6f3ec9ca1840bb2e873.tar.bz2 |
GH-100892: Fix race in clearing `threading.local` (#100922)
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_testcapimodule.c | 41 | ||||
-rw-r--r-- | Modules/_threadmodule.c | 30 |
2 files changed, 56 insertions, 15 deletions
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c777c3e..486988b 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1879,12 +1879,19 @@ temporary_c_thread(void *data) PyThread_release_lock(test_c_thread->exit_event); } +static test_c_thread_t test_c_thread; + static PyObject * -call_in_temporary_c_thread(PyObject *self, PyObject *callback) +call_in_temporary_c_thread(PyObject *self, PyObject *args) { PyObject *res = NULL; - test_c_thread_t test_c_thread; + PyObject *callback = NULL; long thread; + int wait = 1; + if (!PyArg_ParseTuple(args, "O|i", &callback, &wait)) + { + return NULL; + } test_c_thread.start_event = PyThread_allocate_lock(); test_c_thread.exit_event = PyThread_allocate_lock(); @@ -1910,6 +1917,10 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) PyThread_acquire_lock(test_c_thread.start_event, 1); PyThread_release_lock(test_c_thread.start_event); + if (!wait) { + Py_RETURN_NONE; + } + Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(test_c_thread.exit_event, 1); PyThread_release_lock(test_c_thread.exit_event); @@ -1919,13 +1930,32 @@ call_in_temporary_c_thread(PyObject *self, PyObject *callback) exit: Py_CLEAR(test_c_thread.callback); - if (test_c_thread.start_event) + if (test_c_thread.start_event) { PyThread_free_lock(test_c_thread.start_event); - if (test_c_thread.exit_event) + test_c_thread.start_event = NULL; + } + if (test_c_thread.exit_event) { PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + } return res; } +static PyObject * +join_temporary_c_thread(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + 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_CLEAR(test_c_thread.callback); + PyThread_free_lock(test_c_thread.start_event); + test_c_thread.start_event = NULL; + PyThread_free_lock(test_c_thread.exit_event); + test_c_thread.exit_event = NULL; + Py_RETURN_NONE; +} + /* marshal */ static PyObject* @@ -3275,8 +3305,9 @@ static PyMethodDef TestMethods[] = { METH_VARARGS | METH_KEYWORDS}, {"with_tp_del", with_tp_del, METH_VARARGS}, {"create_cfunction", create_cfunction, METH_NOARGS}, - {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_O, + {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS, PyDoc_STR("set_error_class(error_class) -> None")}, + {"join_temporary_c_thread", join_temporary_c_thread, METH_NOARGS}, {"pymarshal_write_long_to_file", pymarshal_write_long_to_file, METH_VARARGS}, {"pymarshal_write_object_to_file", diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index d323ca8..4fbf0e5 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -839,6 +839,11 @@ local_traverse(localobject *self, visitproc visit, void *arg) return 0; } +#define HEAD_LOCK(runtime) \ + PyThread_acquire_lock((runtime)->interpreters.mutex, WAIT_LOCK) +#define HEAD_UNLOCK(runtime) \ + PyThread_release_lock((runtime)->interpreters.mutex) + static int local_clear(localobject *self) { @@ -849,18 +854,23 @@ local_clear(localobject *self) /* Remove all strong references to dummies from the thread states */ if (self->key) { PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); - for(; tstate; tstate = PyThreadState_Next(tstate)) { - if (tstate->dict == NULL) { - continue; - } - PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); - if (v != NULL) { - Py_DECREF(v); - } - else { - PyErr_Clear(); + HEAD_UNLOCK(runtime); + while (tstate) { + if (tstate->dict) { + PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None); + if (v != NULL) { + Py_DECREF(v); + } + else { + PyErr_Clear(); + } } + HEAD_LOCK(runtime); + tstate = PyThreadState_Next(tstate); + HEAD_UNLOCK(runtime); } } return 0; |