Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
Closed
Description
Bug report
Bug description:
There is a time of check to time of use race under free-threading in linecache.py implementation:
Lines 128 to 129 ined039b8
| iffilenameincache: | |
| iflen(cache[filename])!=1: |
Here are two reproducers: one with linecache and second with traceback (a similar usage was originally seen in JAX CI)
- Reproducer with linecache
importlinecacheimportconcurrent.futuresimportthreadingif__name__=="__main__":num_workers=20num_runs=100values=range(10,15)foriinvalues:withopen(f"test_{i}.py","w")ash:h.write("import time\n")h.write("import system\n")barrier=threading.Barrier(num_workers)defclosure():barrier.wait()for_inrange(num_runs):fornameinvalues:linecache.getline(f"test_{name}.py",1)withconcurrent.futures.ThreadPoolExecutor(max_workers=num_workers)asexecutor:futures= []foriinrange(num_workers):futures.append(executor.submit(closure))assertlen(list(f.result()forfinfutures))==num_workers
This gives:
Traceback (most recent call last): File "/project/playground/cpython_checks/linecache_race/repro.py", line 28, in <module> assert len(list(f.result() for f in futures)) == num_workers ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/project/playground/cpython_checks/linecache_race/repro.py", line 28, in <genexpr> assert len(list(f.result() for f in futures)) == num_workers ~~~~~~~~^^ File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/_base.py", line 443, in result return self.__get_result() ~~~~~~~~~~~~~~~~~^^ File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/_base.py", line 395, in __get_result raise self._exception File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/thread.py", line 86, in run result = ctx.run(self.task) File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/thread.py", line 73, in run return fn(*args, **kwargs) File "/project/playground/cpython_checks/linecache_race/repro.py", line 22, in closure linecache.getline(f"test_{name}.py", 1) ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ File "/tmp/cpython-tsan/lib/python3.14t/linecache.py", line 26, in getline lines = getlines(filename, module_globals) File "/tmp/cpython-tsan/lib/python3.14t/linecache.py", line 39, in getlines return cache[filename][2] ~~~~~^^^^^^^^^^KeyError: 'test_11.py'- Reproducer with traceback:
import tracebackimport concurrent.futuresimport threadingif __name__ == "__main__": num_workers = 20 num_runs = 100 barrier = threading.Barrier(num_workers) def closure(): # import test_10 barrier.wait() for _ in range(num_runs): try: raise RuntimeError("STOP") except RuntimeError as e: tb = traceback.extract_stack(e.__traceback__.tb_frame) with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor: futures = [] for i in range(num_workers): futures.append(executor.submit(closure)) assert len(list(f.result() for f in futures)) == num_workersGives the output:
Traceback (most recent call last): File "/project/playground/cpython_checks/linecache_race/repro_traceback.py", line 36, in closure raise RuntimeError("STOP")RuntimeError: STOPDuring handling of the above exception, another exception occurred:Traceback (most recent call last): File "/project/playground/cpython_checks/linecache_race/repro_traceback.py", line 44, in <module> assert len(list(f.result() for f in futures)) == num_workers ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/project/playground/cpython_checks/linecache_race/repro_traceback.py", line 44, in <genexpr> assert len(list(f.result() for f in futures)) == num_workers ~~~~~~~~^^ File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/_base.py", line 443, in result return self.__get_result() ~~~~~~~~~~~~~~~~~^^ File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/_base.py", line 395, in __get_result raise self._exception File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/thread.py", line 86, in run result = ctx.run(self.task) File "/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/thread.py", line 73, in run return fn(*args, **kwargs) File "/project/playground/cpython_checks/linecache_race/repro_traceback.py", line 38, in closure tb = traceback.extract_stack(e.__traceback__.tb_frame) File "/tmp/cpython-tsan/lib/python3.14t/traceback.py", line 264, in extract_stack stack = StackSummary.extract(walk_stack(f), limit=limit) File "/tmp/cpython-tsan/lib/python3.14t/traceback.py", line 457, in extract return klass._extract_from_extended_frame_gen( ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ extended_frame_gen(), limit=limit, lookup_lines=lookup_lines, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ capture_locals=capture_locals) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/tmp/cpython-tsan/lib/python3.14t/traceback.py", line 508, in _extract_from_extended_frame_gen f.line File "/tmp/cpython-tsan/lib/python3.14t/traceback.py", line 377, in line self._set_lines() ~~~~~~~~~~~~~~~^^ File "/tmp/cpython-tsan/lib/python3.14t/traceback.py", line 355, in _set_lines line = linecache.getline(self.filename, lineno).rstrip() ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^ File "/tmp/cpython-tsan/lib/python3.14t/linecache.py", line 26, in getline lines = getlines(filename, module_globals) File "/tmp/cpython-tsan/lib/python3.14t/linecache.py", line 42, in getlines return updatecache(filename, module_globals) File "/tmp/cpython-tsan/lib/python3.14t/linecache.py", line 129, in updatecache if len(cache[filename]) != 1: ~~~~~^^^^^^^^^^KeyError: '/tmp/cpython-tsan/lib/python3.14t/concurrent/futures/thread.py'Context: failure in JAX CI,https://github.com/jax-ml/jax/actions/runs/14770168501/job/41468833801#step:18:4022
@hawkinsp
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux