From a23ded48f5f66dc4b232b8f40d61cf26afbc6688 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 8 Jul 2026 01:03:47 +0300 Subject: [PATCH 1/2] gh-153292: Fix data race in `threading.RLock.__repr__` in FT builds --- .../test_free_threading/test_threading.py | 29 +++++++++++++++++++ ...-07-08-01-03-17.gh-issue-153292.oHDt3l.rst | 1 + Modules/_threadmodule.c | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_free_threading/test_threading.py create mode 100644 Misc/NEWS.d/next/Library/2026-07-08-01-03-17.gh-issue-153292.oHDt3l.rst diff --git a/Lib/test/test_free_threading/test_threading.py b/Lib/test/test_free_threading/test_threading.py new file mode 100644 index 00000000000000..ba67f61c37a5dc --- /dev/null +++ b/Lib/test/test_free_threading/test_threading.py @@ -0,0 +1,29 @@ +import unittest +from test.support import threading_helper + +threading_helper.requires_working_threading(module=True) + + +class TestRlock(unittest.TestCase): + def test_repr_race(self): + # gh-153292 + import _thread + r = _thread.RLock() + + def repr_thread(): + for _ in range(2000): + try: + repr(r) + except Exception: + pass + + def mutate_thread(): + for _ in range(2000): + r.acquire() + r.release() + + threading_helper.run_concurrently([repr_thread, mutate_thread]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-07-08-01-03-17.gh-issue-153292.oHDt3l.rst b/Misc/NEWS.d/next/Library/2026-07-08-01-03-17.gh-issue-153292.oHDt3l.rst new file mode 100644 index 00000000000000..dc363e16007430 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-08-01-03-17.gh-issue-153292.oHDt3l.rst @@ -0,0 +1 @@ +Fix data race in repr of :class:`threading.RLock` in free-threading build. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 7e1884edf5213e..e999fe20287e2d 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1288,7 +1288,7 @@ static PyObject * rlock_repr(PyObject *op) { rlockobject *self = rlockobject_CAST(op); - PyThread_ident_t owner = self->lock.thread; + PyThread_ident_t owner = FT_ATOMIC_LOAD_ULLONG_RELAXED(self->lock.thread); int locked = rlock_locked_impl(self); size_t count; if (locked) { From f36f0d61d3cdd7a259279549556b9706b675da3f Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 8 Jul 2026 10:36:53 +0300 Subject: [PATCH 2/2] Address review --- Lib/test/test_free_threading/test_threading.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Lib/test/test_free_threading/test_threading.py b/Lib/test/test_free_threading/test_threading.py index ba67f61c37a5dc..b5a5ca272b9405 100644 --- a/Lib/test/test_free_threading/test_threading.py +++ b/Lib/test/test_free_threading/test_threading.py @@ -12,10 +12,7 @@ def test_repr_race(self): def repr_thread(): for _ in range(2000): - try: - repr(r) - except Exception: - pass + repr(r) def mutate_thread(): for _ in range(2000):