mirror of https://github.com/python/cpython
gh-120198: Fix race condition when editing __class__ with an audit hook active (GH-120195)
This commit is contained in:
parent
939c201e00
commit
203565b2f9
|
@ -1,3 +1,4 @@
|
||||||
|
import threading
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
"""Unit tests for zero-argument super() & related machinery."""
|
"""Unit tests for zero-argument super() & related machinery."""
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import threading
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from test.support import import_helper
|
from test.support import import_helper, threading_helper
|
||||||
|
|
||||||
|
|
||||||
ADAPTIVE_WARMUP_DELAY = 2
|
ADAPTIVE_WARMUP_DELAY = 2
|
||||||
|
@ -505,6 +506,38 @@ class TestSuper(unittest.TestCase):
|
||||||
for _ in range(ADAPTIVE_WARMUP_DELAY):
|
for _ in range(ADAPTIVE_WARMUP_DELAY):
|
||||||
C.some(C)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix a crash when multiple threads read and write to the same ``__class__`` of an object concurrently.
|
|
@ -6522,7 +6522,6 @@ differs:
|
||||||
static int
|
static int
|
||||||
object_set_class(PyObject *self, PyObject *value, void *closure)
|
object_set_class(PyObject *self, PyObject *value, void *closure)
|
||||||
{
|
{
|
||||||
PyTypeObject *oldto = Py_TYPE(self);
|
|
||||||
|
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
@ -6542,6 +6541,8 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyTypeObject *oldto = Py_TYPE(self);
|
||||||
|
|
||||||
/* In versions of CPython prior to 3.5, the code in
|
/* In versions of CPython prior to 3.5, the code in
|
||||||
compatible_for_assignment was not set up to correctly check for memory
|
compatible_for_assignment was not set up to correctly check for memory
|
||||||
layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just
|
layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just
|
||||||
|
|
Loading…
Reference in New Issue