An optional free-threaded CPython build changes scheduling; a shared application invariant still needs an explicit lock.
Python free-threaded builds: lock the whole shared-state update
Operation contract
Four workers each record forty-seven accepted events. The count is updated inside one lock, so the final count is 188 regardless of scheduling. The program also reads the running process's GIL state instead of guessing from its Python version. The displayed state is from the checked standard build.
Failure boundary
The GIL state can differ on a free-threaded binary and can change when an extension enables it. A successful run under the usual build is not a concurrency stress test. Every writer and reader of a multi-field invariant must follow the same synchronization policy; a thread-safe dictionary operation alone cannot make a larger transaction atomic.
Working program
import sys
from threading import Lock, Thread
accepted_events = 0
event_lock = Lock()
def record_events():
global accepted_events
for _ in range(47):
with event_lock:
accepted_events += 1
workers = [Thread(target=record_events) for _ in range(4)]
for worker in workers:
worker.start()
for worker in workers:
worker.join()
print("gil_enabled", sys._is_gil_enabled())
print("accepted", accepted_events)Output
gil_enabled True
accepted 188Costs and limits
The four workers perform 188 locked increments. Lock contention can serialize this short operation; measure a real workload on each supported interpreter build.
Common Mistakes
- A free-threaded build is not proof that a compound operation is safe without a lock.
- The version number does not reveal the process's current GIL state.
- Do not infer a speedup from a tiny counter fixture.
Connected lessons
- Python locks: protect the complete inventory transition
- Python thread pools: collect results and observe worker failures
- Python compatibility checks: parse syntax and execute contracts as separate evidence
Continue with Python free-threaded diagnostics: separate build capability from current GIL state.
