Describe the bug
The free-threaded (3.14t) full-suite CI job on main fails on one test:
FAILED xrspatial/geotiff/tests/vrt/test_window.py::test_chunks_is_lazy_does_not_call_internal_reader
AssertionError: expected 16 per-chunk decodes after compute, got 15
(run 27626148337, job 81688172532). Every other test passes (9696 passed).
Root cause
The test instruments the internal VRT reader with a counting wrapper:
counter = {'calls': 0}
def counting_read(*args, **kwargs):
counter['calls'] += 1
return real_read(*args, **kwargs)
result.compute() on a 4x4 = 16-chunk dask array runs the 16 decode tasks through dask's threaded scheduler. Under PYTHON_GIL=0 (set on the 3.14t pytest step) those tasks run truly in parallel, and counter['calls'] += 1 is a non-atomic read-modify-write: two threads read the same value and write back the same increment, losing one. So 16 real decodes are counted as 15.
This is a test-instrumentation thread-safety bug, not a product bug. The chunked decode correctly runs 16 times; only the counter miscounts. The test runs in both the fast and full lanes (it is not slow-marked); the race is probabilistic, so it slipped through #3360's 3.14t fast-lane run and first tripped on the main full run.
Fix
Make the counter increment thread-safe (wrap it in a threading.Lock) so the count is exact under both the GIL and the free-threaded interpreter. Only this one test is affected; the other counter-based tests in test_metadata.py / test_overview.py / test_visibility.py assert a single non-concurrent call (== 1) and cannot lose an update.
Notes
Surfaced by the 3.14t lane added in #3360. The job is allowed-failure (continue-on-error), so the overall main run still concludes success, but the red job is noise until this is fixed.
Describe the bug
The free-threaded (
3.14t) full-suite CI job onmainfails on one test:(run 27626148337, job 81688172532). Every other test passes (9696 passed).
Root cause
The test instruments the internal VRT reader with a counting wrapper:
result.compute()on a 4x4 = 16-chunk dask array runs the 16 decode tasks through dask's threaded scheduler. UnderPYTHON_GIL=0(set on the3.14tpytest step) those tasks run truly in parallel, andcounter['calls'] += 1is a non-atomic read-modify-write: two threads read the same value and write back the same increment, losing one. So 16 real decodes are counted as 15.This is a test-instrumentation thread-safety bug, not a product bug. The chunked decode correctly runs 16 times; only the counter miscounts. The test runs in both the fast and full lanes (it is not slow-marked); the race is probabilistic, so it slipped through #3360's
3.14tfast-lane run and first tripped on themainfull run.Fix
Make the counter increment thread-safe (wrap it in a
threading.Lock) so the count is exact under both the GIL and the free-threaded interpreter. Only this one test is affected; the other counter-based tests intest_metadata.py/test_overview.py/test_visibility.pyassert a single non-concurrent call (== 1) and cannot lose an update.Notes
Surfaced by the
3.14tlane added in #3360. The job is allowed-failure (continue-on-error), so the overallmainrun still concludes success, but the red job is noise until this is fixed.