summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_free_threading/test_type.py1
-rw-r--r--Lib/test/test_super.py35
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-06-10-15-07-16.gh-issue-120198.WW_pjO.rst1
-rw-r--r--Objects/typeobject.c3
4 files changed, 38 insertions, 2 deletions
diff --git a/Lib/test/test_free_threading/test_type.py b/Lib/test/test_free_threading/test_type.py
index 6eead19..786336f 100644
--- a/Lib/test/test_free_threading/test_type.py
+++ b/Lib/test/test_free_threading/test_type.py
@@ -1,3 +1,4 @@
+import threading
import unittest
from concurrent.futures import ThreadPoolExecutor
diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py
index 256b416..3ffbe03 100644
--- a/Lib/test/test_super.py
+++ b/Lib/test/test_super.py
@@ -1,9 +1,10 @@
"""Unit tests for zero-argument super() & related machinery."""
import textwrap
+import threading
import unittest
from unittest.mock import patch
-from test.support import import_helper
+from test.support import import_helper, threading_helper
ADAPTIVE_WARMUP_DELAY = 2
@@ -505,6 +506,38 @@ class TestSuper(unittest.TestCase):
for _ in range(ADAPTIVE_WARMUP_DELAY):
C.some(C)
+ @threading_helper.requires_working_threading()
+ def test___class___modification_multithreaded(self):
+ """ Note: this test isn't actually testing anything on its own.
+ It requires a sys audithook to be set to crash on older Python.
+ This should be the case anyways as our test suite sets
+ an audit hook.
+ """
+ class Foo:
+ pass
+
+ class Bar:
+ pass
+
+ thing = Foo()
+ def work():
+ foo = thing
+ for _ in range(5000):
+ foo.__class__ = Bar
+ type(foo)
+ foo.__class__ = Foo
+ type(foo)
+
+
+ threads = []
+ for _ in range(6):
+ thread = threading.Thread(target=work)
+ thread.start()
+ threads.append(thread)
+
+ for thread in threads:
+ thread.join()
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-10-15-07-16.gh-issue-120198.WW_pjO.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-10-15-07-16.gh-issue-120198.WW_pjO.rst
new file mode 100644
index 0000000..8dc8aec
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-10-15-07-16.gh-issue-120198.WW_pjO.rst
@@ -0,0 +1 @@
+Fix a crash when multiple threads read and write to the same ``__class__`` of an object concurrently.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 18b7dfc..2d001b7 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6363,7 +6363,6 @@ differs:
static int
object_set_class(PyObject *self, PyObject *value, void *closure)
{
- PyTypeObject *oldto = Py_TYPE(self);
if (value == NULL) {
PyErr_SetString(PyExc_TypeError,
@@ -6383,6 +6382,8 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
return -1;
}
+ PyTypeObject *oldto = Py_TYPE(self);
+
/* In versions of CPython prior to 3.5, the code in
compatible_for_assignment was not set up to correctly check for memory
layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just