Skip to content

Commit d89f40b

Browse files
[3.15] gh-159458: Fix free-threading data race in datetime strftime century normalization (GH-159520) (gh-159536)
gh-159458: Fix free-threading data race in datetime strftime century normalization (GH-159520) (cherry picked from commit ddc647a) Co-authored-by: Anna <araslanova.anna.a@gmail.com>
1 parent 1f70745 commit d89f40b

3 files changed

Lines changed: 32 additions & 4 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import datetime
2+
import unittest
3+
4+
from test.support import threading_helper
5+
from test.support.threading_helper import run_concurrently
6+
7+
8+
NTHREADS = 10
9+
10+
11+
@threading_helper.requires_working_threading()
12+
class TestDatetime(unittest.TestCase):
13+
def test_concurrent_strftime_normalize_century(self):
14+
d = datetime.date(2026, 4, 12)
15+
results = []
16+
17+
def worker():
18+
results.append(d.strftime("%Y-%m-%d"))
19+
20+
run_concurrently(worker_func=worker, nthreads=NTHREADS)
21+
self.assertEqual(results, ["2026-04-12"] * NTHREADS)
22+
23+
24+
if __name__ == "__main__":
25+
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a ThreadSanitizer data race on the static cache in ``normalize_century()`` of the datetime module under the free-threaded build.

‎Modules/_datetimemodule.c‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,17 +1780,19 @@ inline static int
17801780
normalize_century(void)
17811781
{
17821782
static int cache = -1;
1783-
if (cache < 0) {
1783+
int res = FT_ATOMIC_LOAD_INT_RELAXED(cache);
1784+
if (res < 0) {
17841785
char year[5];
17851786
struct tm date = {
17861787
.tm_year = -1801,
17871788
.tm_mon = 0,
17881789
.tm_mday = 1
17891790
};
1790-
cache = (strftime(year, sizeof(year), "%Y", &date) &&
1791-
strcmp(year, "0099") != 0);
1791+
res = (strftime(year, sizeof(year), "%Y", &date) &&
1792+
strcmp(year, "0099") != 0);
1793+
FT_ATOMIC_STORE_INT_RELAXED(cache, res);
17921794
}
1793-
return cache;
1795+
return res;
17941796
}
17951797

17961798
static PyObject *

0 commit comments

Comments
 (0)