Skip to content

Commit 8eafbc4

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Sync pre-release CPython 3.15 branch from GitHub (2026-07-16)
Summary: Imported python/cpython `3.15.0b3+dev` from upstream rev [`38c3f11`](https://www.github.com/python/cpython/commit/38c3f112c383d1f61a3e2d2d4ff589a5ca570944) (committed 2026-07-16 18:59:43+00:00). # Commit Info - Base: (`3.15.0b3+dev`) - [`fd4c7a8`](https://www.github.com/python/cpython/commit/fd4c7a8d4d46c9b7db920a6b766d367ab22e6a1c) (commit date: 2026-07-15 18:15:24+00:00) - Imported: (`3.15.0b3+dev`) - [`38c3f11`](https://www.github.com/python/cpython/commit/38c3f112c383d1f61a3e2d2d4ff589a5ca570944) (commit date: 2026-07-16 18:59:43+00:00) # Noteworthy file changes - Low-signal files (3 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GErqtCxXhL3TGGYGAIesG1Zxc5Jlbr0LAAAz Differential Revision: D112487641 fbshipit-source-id: 56170695a5b8050ffbb764a1ccfd5b1e54358d15
1 parent 6df4ae1 commit 8eafbc4

8 files changed

Lines changed: 63 additions & 4 deletions

File tree

Lib/test/test_free_threading/test_gc.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,35 @@ def setter():
124124
finally:
125125
gc.set_threshold(*current_threshold)
126126

127+
def test_get_count(self):
128+
class CyclicReference:
129+
def __init__(self):
130+
self.ref = self
131+
132+
NUM_ALLOCATORS = 7
133+
NUM_READERS = 1
134+
NUM_THREADS = NUM_ALLOCATORS + NUM_READERS
135+
NUM_ITERS = 1000
136+
137+
barrier = threading.Barrier(NUM_THREADS)
138+
139+
def allocator():
140+
barrier.wait()
141+
for _ in range(NUM_ITERS):
142+
CyclicReference()
143+
144+
145+
def reader():
146+
barrier.wait()
147+
for _ in range(NUM_ITERS):
148+
gc.get_count()
149+
150+
threads = [Thread(target=allocator) for _ in range(NUM_ALLOCATORS)]
151+
threads.extend(Thread(target=reader) for _ in range(NUM_READERS))
152+
153+
with threading_helper.start_threads(threads):
154+
pass
155+
127156

128157
if __name__ == "__main__":
129158
unittest.main()

Lib/test/test_free_threading/test_sys.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@
44

55

66
class SysModuleTest(unittest.TestCase):
7+
@unittest.skipUnless(hasattr(sys, "setdlopenflags"),
8+
"test needs sys.setdlopenflags()")
9+
def test_dlopenflags_concurrent(self):
10+
# gh-151644: getdlopenflags() and setdlopenflags() must be safe to
11+
# call concurrently in free-threaded builds.
12+
original = sys.getdlopenflags()
13+
self.addCleanup(sys.setdlopenflags, original)
14+
15+
# Use a small set of known-valid flag values to avoid integer overflow.
16+
flag_values = [1, 2, 256, 257]
17+
18+
def worker(worker_id):
19+
for i in range(20_000):
20+
if worker_id % 2 == 0:
21+
sys.getdlopenflags()
22+
else:
23+
sys.setdlopenflags(flag_values[worker_id % len(flag_values)])
24+
25+
workers = [lambda i=i: worker(i) for i in range(6)]
26+
threading_helper.run_concurrently(workers)
27+
728
def test_int_max_str_digits_thread(self):
829
# gh-151218: Check that it's safe to call get_int_max_str_digits() and
930
# set_int_max_str_digits() in parallel. Previously, this test triggered
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a potential deadlock in :c:func:`PyUnicode_InternFromString` and other
2+
interning functions in the :term:`free-threaded build` when called from C++
3+
static local initializers.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a data race in the free-threaded build when :func:`gc.get_count` reads
2+
the young generation allocation count while another thread updates it.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix a data race in :func:`sys.setdlopenflags` and :func:`sys.getdlopenflags`
2+
when called concurrently in the free-threaded build. The underlying
3+
``_PyImport_GetDLOpenFlags`` and ``_PyImport_SetDLOpenFlags`` functions now
4+
use atomic load/store operations.

Modules/gcmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ gc_get_count_impl(PyObject *module)
233233
gcstate->generations[2].count);
234234
#else
235235
return Py_BuildValue("(iii)",
236-
gcstate->young.count,
236+
_Py_atomic_load_int_relaxed(&gcstate->young.count),
237237
gcstate->old[0].count,
238238
gcstate->old[1].count);
239239
#endif

Objects/unicodeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14344,7 +14344,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
1434414344
}
1434514345
#endif
1434614346

14347-
FT_MUTEX_LOCK(INTERN_MUTEX);
14347+
FT_MUTEX_LOCK_FLAGS(INTERN_MUTEX, _Py_LOCK_DONT_DETACH);
1434814348
PyObject *t;
1434914349
{
1435014350
int res = PyDict_SetDefaultRef(interned, s, s, &t);

Python/import.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -911,13 +911,13 @@ _PyImport_SwapPackageContext(const char *newcontext)
911911
int
912912
_PyImport_GetDLOpenFlags(PyInterpreterState *interp)
913913
{
914-
return DLOPENFLAGS(interp);
914+
return FT_ATOMIC_LOAD_INT_RELAXED(DLOPENFLAGS(interp));
915915
}
916916

917917
void
918918
_PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val)
919919
{
920-
DLOPENFLAGS(interp) = new_val;
920+
FT_ATOMIC_STORE_INT_RELAXED(DLOPENFLAGS(interp), new_val);
921921
}
922922
#endif // HAVE_DLOPEN
923923

0 commit comments

Comments
 (0)