GeezerVison is showing github.com — Audit all built-in modules for thread safety · Issue #116738 · python/cpython
Audit all built-in modules for thread safety #116738DescriptionFeature or enhancement
Proposal:
We should audit every built-in module for thread safety and make any necessary fixes. This can be done separately from tagging the modules as safe using Py_mod_gil; any data races in built-in modules are considered bugs in the free-threaded build and not incompatibilities.
Below is a list of all source files that contain at least one module definition, as defined by files that contain a call to PyModule_Create(), an array of PyModuleDef_Slots, or a PyModExport_* function definition. If you'd like to help out with this task, take a look at one of the incomplete files. If the conversion is trivial, check it off and attach the PR to this issue. Otherwise, convert the line into an issue and assign it to yourself.
For modules split across multiple files, the audit should include their implementation helpers, not just the file defining the module.
Every module is different, but here are a few high-level points to guide the work:
- Use
PyMutex as the basic unit of locking.
- If you need to hold a lock for a longer period of time, especially across calls that may reenter Python code or acquire other locks, use critical sections.
- If the module you're looking at is a thin wrapper around related code elsewhere in CPython (e.g.,
Modules/_codecsmodule.c and Python/codecs.c), you can also audit/convert the related non-module code. Otherwise, try to contain your work to just the module code, and create separate issues for any dependencies that aren't thread-safe.
- Remember that C API functions are generally expected to handle thread-safety internally. C code that operates on Python objects using only C API calls is usually thread-safe by default (but look out for code that reads or modifies
PyObject*s in C structs, since that needs synchronization).
- Watch out for functions/macros that return borrowed references, like
PyList_GetItem() or PyDict_GetItem(). If other threads could have references to the object, prefer functions like PyList_GetItemRef() or PyDict_GetItemRef() that return owned references (and will safely raise an error if the index/key doesn't exist).
Files to audit
Checked items record completed initial audits or conversions; follow-up issues are listed below. Unchecked items may have partial fixes; their links include completed work and outstanding issues.
- Modules/_abc.c
- Modules/arraymodule.c - Python
array module is not free-thread safe. #128942, gh-128942: make arraymodule.c free-thread safe (lock-free) #130771, Need to defer GC tracking of array.array construction #153083, arraymodule, _decimal: possible lazy import caches reference leaks in free-threaded builds #149006
- Modules/_asynciomodule.c - Audit asyncio thread safety #128002
- Modules/atexitmodule.c - Running
atexit from threads in free-threading build segfaults #126907
- Modules/binascii.c
- Modules/_bisectmodule.c
- Modules/blake2module.c - Make hashlib related modules thread-safe without the GIL #111916, Replace built-in hashlib with verified implementations from HACL* #99108
- Modules/_bz2module.c
- Modules/cjkcodecs/cjkcodecs.h
- Modules/cjkcodecs/multibytecodec.c - Data race in the
codec.errors setter (codecctx_errors_set) on a shared stateful codec #152767, Data race in Modules/cjkcodecs/multibytecodec.c #154577, Data races in CJK multibyte codec state under free-threading #156169
- Modules/cmathmodule.c
- Modules/_codecsmodule.c
- Modules/_collectionsmodule.c - Make collections.deque thread-safe in
--disable-gil builds #112050
- Modules/_csv.c
- Modules/_ctypes/_ctypes.c -
ctypes thread safety auditing (and fixing) #127945, ctypes pointer writes are not thread safe #128182
- Modules/_ctypes/_ctypes_test.c
- Modules/_cursesmodule.c
- Modules/_curses_panel.c
- Modules/_datetimemodule.c - data race on hashing of datetime objects #139774, Free-threading: data race on static cache in
_datetime.normalize_century() #159458
- Modules/_dbmmodule.c
- Modules/_decimal/_decimal.c - Data race in
Decimal.__hash__() #154037, data races in decimal module with global context #141148, _decimal: mpd_context_t::status/traps mutated non-atomically leading to data race #149142, arraymodule, _decimal: possible lazy import caches reference leaks in free-threaded builds #149006
- Modules/_elementtree.c - UAF:
xml.etree.ElementTree.Element.remove when concurrent mutations happen #126033, UAF: xml.etree.ElementTree.Element.find* when concurrent mutations happen #126037, UAF: xml.etree.ElementTree.Element.__deepcopy__ when concurrent mutations happen #133009, Null pointer dereference in element_[ass_]subscr via re-entrant calls #143200, Make xml.etree.ElementTree.Element usable on free-threaded builds #146022, Improve thread-safety in ElementObjects #149965, 22 free-threading race conditions #149816, Free-threading data races in CPython: 15 findings (9 new + 6 residual of existing FT work) #153852
- Modules/errnomodule.c
- Modules/faulthandler.c - Crash when using _Py_DumpTracebackThreads #128400, Data race in
faulthandler.enable() and faulthandler.disable() with free-threading #151363, faulthandler: data races in enable()/disable() and dump_traceback_later() under free threading #151475
- Modules/fcntlmodule.c
- Modules/_functoolsmodule.c
- Modules/gcmodule.c
- Modules/_gdbmmodule.c
- Modules/grpmodule.c
- Modules/_hashopenssl.c
- Modules/_heapqmodule.c
- Modules/hmacmodule.c - Replace built-in hashlib with verified implementations from HACL* #99108
- Modules/_interpchannelsmodule.c - TSAN race in _waiting_release #123828, test_concurrent_futures.test_interpreter_pool failing #125716
- Modules/_interpqueuesmodule.c - test_concurrent_futures.test_interpreter_pool failing #125716
- Modules/_interpretersmodule.c (including
Python/crossinterp.c) - 22 free-threading race conditions #149816
- Modules/_io/_iomodule.c
- Modules/itertoolsmodule.c - Make TSAN tests pass with the GIL disabled in free-threaded builds #117657, itertools.tee: concurrent iteration crashes the free-threaded build #153062, tsan-006: itertoolsmodule.c: count.__repr__ plain-reads cnt #153908, Make concurrent iteration over pairwise, combinations, permutations, cwr, product, etc. from itertools safe under free-threading #123471,
groupby_next data race on free-threaded builds #150791, Race condition in itertools.islice under free-threading #151409, itertoolsmodule: free-threading use after free bug in counting slow mode #153981, itertools.count use-after-free via re-entrant step.__radd__ #154670
- Modules/_json.c - gh-116738: Make _json module safe in the free-threading build #119438
- Modules/_localemodule.c - Thread-unsafe libc functions #127081
- Modules/_lsprof.c
- Modules/_lzmamodule.c
- Modules/mathintegermodule.c
- Modules/mathmodule.c
- Modules/md5module.c
- Modules/mmapmodule.c
- Modules/_multiprocessing/multiprocessing.c (including
semaphore.c) - Make multiprocessing SemLock thread-safe in the free-threaded build #117435, Make TSAN tests pass with the GIL disabled in free-threaded builds #117657
- Modules/_multiprocessing/posixshmem.c
- Modules/_opcode.c
- Modules/_operator.c -
methodcaller is not thread-safe (or re-entrant) #127065
- Modules/overlapped.c
- Modules/_pickle.c - Use-after-free in
save_picklebuffer via re-entrant buffer_callback and __bool__ #143308, Use-after-free in pickle BUILD via re-entrant __setitem__ #143638, Segfault in _pickle.c:batch_dict_exact when pickling dict with concurrent mutation (free-threading) #146452, 22 free-threading race conditions #149816, Missing critical section for PyDict_Next in _pickle.c #150157, Segfault in _Unpickler_MemoPut (_pickle.c) from Unpickler.memo assignment with free-threading #150505
- Modules/posixmodule.c - Use-after-free in
parse_envlist via re-entrant env.keys() or env.values() #143309, Segfault between os.scandir iterator's close() and __next__() on the dirp handle #152754, Calling os.strerror() in parallel is not thread safe on FreeBSD #158893, posixmodule uses PyOS_setsig to save & restore signal handlers #114727, Thread-unsafe libc functions #127081
- Modules/_posixsubprocess.c
- Modules/pwdmodule.c
- Modules/pyexpat.c
- Modules/_queuemodule.c - Make queue.SimpleQueue thread-safe in
--disable-gil builds #113884
- Modules/_randommodule.c - Make
random.Random thread-safe in --disable-gil builds #112071
- Modules/readline.c - Segfault/aborts calling
readline.set_completer_delims in threads in a free-threaded build #126895, Data race in readline.get_completer() reads state->completer without the critical section #153291, Data race of should_auto_add_history in file Modules/readline.c #154539
- Modules/_remote_debugging/module.c
- Modules/resource.c
- Modules/_scproxy.c
- Modules/selectmodule.c -
select.poll() objects are not thread-safe in the free-threaded build #121592
- Modules/sha1module.c
- Modules/sha2module.c
- Modules/sha3module.c
- Modules/signalmodule.c
- Modules/socketmodule.c - Make
socket module thread safe #128277
- Modules/_sqlite/module.c - sqlite3 converter lookup can crash during concurrent registry updates in free-threaded builds #155781
- Modules/_sre/sre.c
- Modules/_ssl.c - Segfault on Python 3.13.0rc3 (free-threaded) with
requests #124984
- Modules/_stat.c
- Modules/_statisticsmodule.c
- Modules/_struct.c - Make
_struct module thread-safe in --disable-gil builds #112062
- Modules/_suggestions.c
- Modules/symtablemodule.c
- Modules/_sysconfig.c
- Modules/syslogmodule.c - Make syslog thread-safe in
--disable-gil builds #117440
- Modules/termios.c
- Modules/_testbuffer.c
- Modules/_testcapimodule.c
- Modules/_testcapi/datetime.c
- Modules/_testcapi/module.c
- Modules/_testclinic.c
- Modules/_testclinic_limited.c
- Modules/_testimportmultiple.c
- Modules/_testinternalcapi.c
- Modules/_testlimitedcapi.c
- Modules/_testlimitedcapi/slots.c
- Modules/_testmultiphase.c
- Modules/_testsinglephase.c
- Modules/_threadmodule.c - Make
_threadmodule.c thread-safe in --disable-gil builds #114271
- Modules/timemodule.c
- Modules/_tkinter.c - Data race on
event_tstate in _tkinter.c under free-threading #153020
- Modules/_tracemalloc.c (including
Python/tracemalloc.c) - Race condition in tracemalloc causes segfaults #128679, Segmentation fault during interpreter finalization in PyTraceMalloc_Untrack #129185, PyRefTracer_SetTracer and PyRefTracer_GetTracer are not thread safe in the free threading build #137992, Potential deadlock in test_tracemalloc with the FT build and tail-call dispatch. #139116, Crash in _io_TextIOWrapper_write_impl when running tracemalloc.clear_traces in threads #144763, tracemalloc raw-domain allocator hook violates PYMEM_DOMAIN_RAW contract #155725, tracemalloc aborts when run from threads in no-gil #126315
- Modules/_typesmodule.c
- Modules/_typingmodule.c
- Modules/unicodedata.c - Segfault from
unicodedata.iter_graphemes on a free threaded build #145202
- Modules/_uuidmodule.c
- Modules/_weakref.c
- Modules/_winapi.c
- Modules/xxlimited_35.c
- Modules/xxlimited_3_13.c
- Modules/xxlimited.c
- Modules/xxmodule.c
- Modules/xxsubtype.c
- Modules/_xxtestfuzz/_xxtestfuzz.c
- Modules/zlibmodule.c
- Modules/_zoneinfo.c
- Modules/_zstd/_zstdmodule.c (including
compressor.c, decompressor.c, and zstddict.c) - test_zstd failed on ubuntu with free-threading #133885, Free-threading data races in CPython: 15 findings (9 new + 6 residual of existing FT work) #153852 (ZstdCompressor.last_mode race, disputed in gh-133885: fix TSAN-0002 race of last_mode in gh-153852 #154407)
- Objects/genobject.c - async generator allows concurrent access via async_gen_athrow_throw and async_gen_asend_throw #117881, SIGSEGV with generators in free-threaded build #120321, Data races with frame objects #144446, Free-threading: concurrent send() on a shared aclose() awaitable can resume the async generator instead of closing it #160347
- Objects/unicodeobject.c (
_string module only)
- PC/_testconsole.c
- PC/_wmimodule.cpp
- PC/msvcrtmodule.c
- PC/winreg.c
- PC/winsound.c
- Python/_contextvars.c (including
Python/context.c and Python/hamt.c) - ContextVars are not thread-safe in the free-threaded build #121546, gh-116738: make entering of contextvars.Context thread safe #143074, Data race with Context ctx_vars under free-threading #155519, Sharing a contextvars.Context iterator across threads crashes (HAMT iterator cursor corruption) under free-threading #154535, PyContext watcher registry has data races in free-threaded builds #155619
- Python/bltinmodule.c - Builtin zip method is not safe under free-threading #123271
- Python/import.c - CPython should not assume that pthread_self returns the same value in fork parent and child #126688, free-threading: PyImport_AddModuleRef race condition #137422, importlib race condition allows stale module ref to escape on failure #143650, Data race in
_PyImport_SetDLOpenFlags when sys.setdlopenflags() is called concurrently #151644
- Python/instrumentation.c
- Python/marshal.c - Make
set thread-safe in --disable-gil builds #112069, Use _PyObject_IsUniquelyReferenced instead of Py_REFCNT(x) == 1 for free-threaded compatibility #140061, Concurrent compilation changes marshal output in free-threaded builds #156504, Free-threading data races in CPython: 15 findings (9 new + 6 residual of existing FT work) #153852
- Python/Python-ast.c - Fix thread-safety of interpreter cached object
str_replace_inf (free threading) #125268
- Python/Python-tokenize.c - Using internal tokenize module's TokenizerIter in multiple threads crashes #120317
- Python/sysmodule.c -
sys._setprofileallthreads race condition #137400, Py_IsInitialized() can return 1 before Py_InitializeEx() has completed. #146302, Data race in sys_set_flag when sys.set_int_max_str_digits() is called concurrently with free-threaded build #151218, Data race in _PyImport_SetDLOpenFlags when sys.setdlopenflags() is called concurrently #151644, Data race: sys.addaudithook() lazily creates interp->audit_hooks with no lock #154431 (fix reverted in gh-154431: Revert "gh-154431: Fix data race in sys.audithook (#154462)" #154769), Data race between sys._current_exceptions() and a concurrently attaching thread #152741, Free-threading data races in CPython: 15 findings (9 new + 6 residual of existing FT work) #153852
- Python/_warnings.c - Make
_warnings.c thread-safe in free-threaded build #116664
Follow-up issues
_asyncio: AsyncIO thread-safety issue in Python 3.14's free-threading #159449
collections: collections.deque is not atomic during copy #144809, Data race between deque.index and deque mutators on Py_SIZE with free-threading #150750, Concurrent calls to Counter.update can cause a use after free #151633, defaultdict reads default_factory as a borrowed reference in defdict_repr / defdict_missing under free-threading #154527
ctypes: Data race: ctypes PyCData_NewGetBuffer reads b_ptr without the critical section _ctypes_resize holds #154524, ctypes: crash when deleting Pointer.contents on the free-threaded build #156124, ctypes array cache can return distinct types when types are created concurrently #160978, 22 free-threading race conditions #149816
functools: Data race between compare_generic and insert_combined_dict under free-threading #132641, Free-threaded CPython 3.14: concurrent functools.partial.__setstate__ crashes with repr(), calls, or another __setstate__ #157841
gc: Data race between gc.get_stats() and a concurrent collection under free-threading #151646, Data race on the GC debug flag (gc.set_debug/get_debug) in free-threading builds #153014
hashlib: Race in py_digest_by_name, _hashopenssl.c under free-threading #128657, Smarter use of a mutex in incremental HMAC and hash functions #135239, Ensure that BLAKE-2 objects are fully initialized before being GC-tracked #155830, Ensure that HACL* states are not read concurrently #155835
heapq: data race between lock free list reads and heapq #135557
hmac: Smarter use of a mutex in incremental HMAC and hash functions #135239, HACL* update() errors are handled without a thread state #159786
io.BytesIO: Use-after-free in _io.BytesIO.writelines via re-entrant __buffer__ close #143378, Data race between resize_buffer_lock_held and _Py_DecRefShared on a shared BytesIO buffer #151640, Data race between BytesIO.__setstate__ and lock-free LOAD_ATTR method #153290
io.StringIO: Segfault from calling StringIO methods in threads on free-threading debug build #135410, Use-after-free with a concurrent stringio_iternext #153296
io.TextIOWrapper: Crash when calling textiowrapper_iternext and writing to a text file simultaneously in ft build #134908, Possible data race in _io_IncrementalNewlineDecoder_reset_impl in textio.c #144777, Data race: TextIOWrapper.detach() stores self->buffer = NULL non-atomically #154523
json: _json: Use After Free in encoder_encode_key_value() - borrowed 'key' used after free #145244
select: Data race on kqueue.kqfd between close() and the closed getter with free-threading build #151364
socket: Free threading: Data race on a socket's timeout #153935, Thread-unsafe libc functions #127081
ssl: Data races in OpenSSL bindings #143756
struct: ThreadSanitizer: data race in _struct module initialization with InterpreterPoolExecutor (free-threading build) #140260
sys.monitoring: Free threading: Data race on tool_id in sys.monitoring #154348, Free-threading data races in CPython: 15 findings (9 new + 6 residual of existing FT work) #153852
syslog: syslog cross-interpreter refcount race on static S_ident_o #158929, Free-threaded: concurrent syslog.openlog() and sys.argv[0] assignment crash the interpreter #159864
_thread: threading.local() implementation is not thread-safe in the free-threaded build #120973, Data race in repr() of _thread.RLock #153292
warnings: Interpreter shutdown can deadlock when a daemon thread holds the warnings lock #159485, Use critical sections in _warnings.c rather than mutex #160397
Cross-module tracking issues
Has this already been discussed elsewhere?
I have already discussed this feature proposal on Discourse
Links to previous discussion of this feature:
https://peps.python.org/pep-0703/, especially this section
https://peps.python.org/pep-0779/ and its Steering Council acceptance.
Further reading
Linked PRs
Reacted by Erlend E. Aasland, stonebig, An Long, Michael H and Henry Cai Reacted by stonebig Activitympage commented on May 28, 2024 @swtaarrs - Is it OK if I remove you as the owner? swtaarrs commented on May 29, 2024 Yep, I won't be doing any more work on this 😞 raven-dev-ops commented on Dec 28, 2025 Audited Modules/binascii.c for free-threaded builds (gh-116738) and added regression coverage to ensure importing binascii with -X gil=0 does not enable the GIL, plus a multi-thread stress test.
PR: #143234 ngoldbaum commented on Oct 30, 2026 I just did a (clanker-assisted) update pass on the issue.
I updated the source-file listing to reflect CPython main. I also clarified the scope a little in the intro, noting that PyModExport_* definitions are a thing now and that some modules span several compilation units.
I added links to completed and outstanding audit work, and grouped later fixes and bugs under an expanded “Follow-up issues” section. That gives the initial audit a clear completion point while acknowledging that we are imperfect and bugs happen: checked boxes mean the initial audit or conversion is complete.
I also gave _io separate helper-file checkboxes (ping @cmaloney @KowalskiThomas), listed cross-module trackers separately, and removed the stale “Completed Issues” block. Finally, I added links to PEP 779, the free-threaded extension porting HOWTO, the community free-threading guide, and @nascheme's guidance. Reacted by Cody Maloney Reacted by Neil Schemenauer
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|