https://github.com/python/cpython/commit/ddc647a3410f05e99b2c65d40283e4ad6d903bef
commit: ddc647a3410f05e99b2c65d40283e4ad6d903bef
branch: main
author: Anna <[email protected]>
committer: encukou <[email protected]>
date: 2026-10-16T16:16:09+02:00
summary:

gh-159458: Fix free-threading data race in datetime strftime century 
normalization (GH-159520)

files:
A Lib/test/test_free_threading/test_datetime.py
A Misc/NEWS.d/next/Library/2026-10-16-10-25-22.gh-issue-159458.q02EnY.rst
M Modules/_datetimemodule.c

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 000000000000000..6cea76b32d9ffcc
--- /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 000000000000000..6565f4265b5877c
--- /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 bd76b3bd81cce40..dbd14f1d7070772 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -1783,17 +1783,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 *

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to