--- /dev/null
+import unittest
+import copy
+
+from test.support import threading_helper
+
+threading_helper.requires_working_threading(module=True)
+class ExceptionTests(unittest.TestCase):
+ def test_setstate_data_race(self):
+ E = Exception()
+
+ def func():
+ for i in range(100):
+ setattr(E, 'x', i)
+ copy.copy(E)
+
+ threading_helper.run_concurrently(func, nthreads=4)
--- /dev/null
+Fix data race in :class:`BaseException` when an exception is copied while being mutated.
{
PyObject *return_value = NULL;
- Py_BEGIN_CRITICAL_SECTION(self);
+ Py_BEGIN_CRITICAL_SECTION(state);
return_value = BaseException___setstate___impl((PyBaseExceptionObject *)self, state);
Py_END_CRITICAL_SECTION();
return return_value;
}
-/*[clinic end generated code: output=fcf70b3b71f3d14a input=a9049054013a1b77]*/
+/*[clinic end generated code: output=e63b88d0443b4f92 input=a9049054013a1b77]*/
*/
/*[clinic input]
-@critical_section
+@critical_section state
BaseException.__setstate__
state: object
/
static PyObject *
BaseException___setstate___impl(PyBaseExceptionObject *self, PyObject *state)
-/*[clinic end generated code: output=f3834889950453ab input=5524b61cfe9b9856]*/
+/*[clinic end generated code: output=f3834889950453ab input=f9b1aea70382cdb6]*/
{
PyObject *d_key, *d_value;
Py_ssize_t i = 0;