From 4e0750d272cb7fbe457605b5b0c18f2b7cd578fb Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 16 Oct 2026 16:16:09 +0200 Subject: [PATCH] gh-159458: Fix free-threading data race in datetime strftime century normalization (GH-159520) (cherry picked from commit ddc647a3410f05e99b2c65d40283e4ad6d903bef) Co-authored-by: Anna --- Lib/test/test_free_threading/test_datetime.py | 25 +++++++++++++++++++ ...-10-16-10-25-22.gh-issue-159458.q02EnY.rst | 1 + Modules/_datetimemodule.c | 10 +++++--- 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 Lib/test/test_free_threading/test_datetime.py create mode 100644 Misc/NEWS.d/next/Library/2026-10-16-10-25-22.gh-issue-159458.q02EnY.rst diff --git a/Lib/test/test_free_threading/test_datetime.py b/Lib/test/test_free_threading/test_datetime.py new file mode 100644 index 00000000000000..6cea76b32d9ffc --- /dev/null +++ b/Lib/test/test_free_threading/test_datetime.py @@ -0,0 +1,25 @@ +import datetime +import unittest + +from test.support import threading_helper +from test.support.threading_helper import run_concurrently + + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class TestDatetime(unittest.TestCase): + def test_concurrent_strftime_normalize_century(self): + d = datetime.date(2026, 4, 12) + results = [] + + def worker(): + results.append(d.strftime("%Y-%m-%d")) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(results, ["2026-04-12"] * NTHREADS) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-10-16-10-25-22.gh-issue-159458.q02EnY.rst b/Misc/NEWS.d/next/Library/2026-10-16-10-25-22.gh-issue-159458.q02EnY.rst new file mode 100644 index 00000000000000..6565f4265b5877 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-16-10-25-22.gh-issue-159458.q02EnY.rst @@ -0,0 +1 @@ +Fix a ThreadSanitizer data race on the static cache in ``normalize_century()`` of the datetime module under the free-threaded build. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index d0f2e8c7dcabe4..fc07b4b7bc01fb 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1780,17 +1780,19 @@ inline static int normalize_century(void) { static int cache = -1; - if (cache < 0) { + int res = FT_ATOMIC_LOAD_INT_RELAXED(cache); + if (res < 0) { char year[5]; struct tm date = { .tm_year = -1801, .tm_mon = 0, .tm_mday = 1 }; - cache = (strftime(year, sizeof(year), "%Y", &date) && - strcmp(year, "0099") != 0); + res = (strftime(year, sizeof(year), "%Y", &date) && + strcmp(year, "0099") != 0); + FT_ATOMIC_STORE_INT_RELAXED(cache, res); } - return cache; + return res; } static PyObject *