A generator can exit successfully and report a cache hit for the requested pipeline while leaving an older pipeline's output on disk. This reproduces with a small CPU-only generator and either object or static-library output on Windows.
Reproducer
Environment: Windows x64, MSVC 19.42, SDK 10.0.22621.0, halide-bin==22.0.0.dev390, shared Halide.dll. The relevant implementation is unchanged at a95f4de3b2b9279830e9b2c90983461ab2a4d8a1.
The standalone reproducer uses a public-API generator computes output(x) = input(x) + offset, where offset is a GeneratorParam. The driver launches a fresh process for every generation, keeps function/output names and paths fixed, and sets HL_DEBUG_CODEGEN=1, HL_CACHE_DIR and HL_CACHE_MAX_SIZE=2G. No GPU or private source is required.
-
Generate offsets 1 (A) and 2 (B) into the same output pathname, populating both cache entries. Verify that their keys and artifact hashes differ.
-
Restore A.
-
Open the output with CreateFileW(GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING), allowing reads but denying writes and deletion.
-
While holding that handle, request B from the populated cache.
-
Close the handle and repeat the B request.
Observed object-output results, SHA-256 prefixes:
| Request | Exit | Cache message | Output hash |
|---|---:|---|---|
| A control | 0 | stores A key 43c7318ed008 | A: 3063cd74ca08 |
| B control | 0 | stores B key 06a1f21736b7 | B: ef9bbb0410fc |
| B while A is locked | 0 | hit for B key 06a1f21736b7 | A: 3063cd74ca08 |
| B after unlock | 0 | hit for B key 06a1f21736b7 | B: ef9bbb0410fc |
Static-library output exhibits the same failure. Full hashes and build/run commands are in the linked reproducer. The driver always closes handles in a finally block.
Platform scope
The faulty restore code is shared across platforms. The same failure has also been reproduced on macOS with an immutable destination archive. The linked driver uses Windows file-sharing locks; Linux has not been tested.
Expected behavior
If an output cannot be replaced, the invocation must not report successful restoration. It should report the output path and file error, fail the command, and clean pending temporary files. Successful restoration should continue to work after the obstruction is removed.
Cause and proposed fix
In GeneratorCache::try_restore, a failed rename falls back to copy_file. Its error is not checked before cleanup overwrites the error code; execution then logs a hit and returns true.
I have a small patch that checks the fallback copy, preserves its error message, removes pending temporary files, and raises a user error before reaching the hit path. It also updates the partial-output contract and extends test/correctness/generator_cache.cpp with a nonempty-directory obstruction that does not depend on platform permission semantics.
Returning a miss deserves care: a separately reproduced Windows COFF-writer bug can make the subsequent fresh compile silently succeed with stale bytes. The proposed restore patch therefore fails explicitly once output replacement has failed. Successful copy fallback remains allowed.
The new regression fails with the original wheel and passes with a full rebuilt shared Halide DLL containing the restore fix. The cache, compile-to and multitarget correctness tests pass, as do both tests in the normal CMake cache integration project. Locked .obj and .lib requests fail without reporting a hit, and unlocking restores the correct bytes. The focused fix and full-build details are in PR #9438; patched macOS/Linux and static-linkage validation remain outstanding.
This report establishes an output-replacement error-handling defect. The A/B keys differ; no semantic-key collision is claimed. This is a follow-up to the cache introduced in #9266.
The independently reproduced COFF archive producer failure is tracked in #9439, with its fix in #9440. That failure can publish incorrect bytes on a cache miss; this issue concerns a false hit during restoration.
A generator can exit successfully and report a cache hit for the requested pipeline while leaving an older pipeline's output on disk. This reproduces with a small CPU-only generator and either object or static-library output on Windows.
Reproducer
Environment: Windows x64, MSVC 19.42, SDK 10.0.22621.0,
halide-bin==22.0.0.dev390, sharedHalide.dll. The relevant implementation is unchanged ata95f4de3b2b9279830e9b2c90983461ab2a4d8a1.The standalone reproducer uses a public-API generator computes
output(x) = input(x) + offset, whereoffsetis a GeneratorParam. The driver launches a fresh process for every generation, keeps function/output names and paths fixed, and setsHL_DEBUG_CODEGEN=1,HL_CACHE_DIRandHL_CACHE_MAX_SIZE=2G. No GPU or private source is required.Generate offsets 1 (A) and 2 (B) into the same output pathname, populating both cache entries. Verify that their keys and artifact hashes differ.
Restore A.
Open the output with
CreateFileW(GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING), allowing reads but denying writes and deletion.While holding that handle, request B from the populated cache.
Close the handle and repeat the B request.
Observed object-output results, SHA-256 prefixes:
| Request | Exit | Cache message | Output hash |
|---|---:|---|---|
| A control | 0 | stores A key
43c7318ed008| A:3063cd74ca08|| B control | 0 | stores B key
06a1f21736b7| B:ef9bbb0410fc|| B while A is locked | 0 | hit for B key
06a1f21736b7| A:3063cd74ca08|| B after unlock | 0 | hit for B key
06a1f21736b7| B:ef9bbb0410fc|Static-library output exhibits the same failure. Full hashes and build/run commands are in the linked reproducer. The driver always closes handles in a
finallyblock.Platform scope
The faulty restore code is shared across platforms. The same failure has also been reproduced on macOS with an immutable destination archive. The linked driver uses Windows file-sharing locks; Linux has not been tested.
Expected behavior
If an output cannot be replaced, the invocation must not report successful restoration. It should report the output path and file error, fail the command, and clean pending temporary files. Successful restoration should continue to work after the obstruction is removed.
Cause and proposed fix
In
GeneratorCache::try_restore, a failed rename falls back tocopy_file. Its error is not checked before cleanup overwrites the error code; execution then logs a hit and returns true.I have a small patch that checks the fallback copy, preserves its error message, removes pending temporary files, and raises a user error before reaching the hit path. It also updates the partial-output contract and extends
test/correctness/generator_cache.cppwith a nonempty-directory obstruction that does not depend on platform permission semantics.Returning a miss deserves care: a separately reproduced Windows COFF-writer bug can make the subsequent fresh compile silently succeed with stale bytes. The proposed restore patch therefore fails explicitly once output replacement has failed. Successful copy fallback remains allowed.
The new regression fails with the original wheel and passes with a full rebuilt shared Halide DLL containing the restore fix. The cache, compile-to and multitarget correctness tests pass, as do both tests in the normal CMake cache integration project. Locked
.objand.librequests fail without reporting a hit, and unlocking restores the correct bytes. The focused fix and full-build details are in PR #9438; patched macOS/Linux and static-linkage validation remain outstanding.This report establishes an output-replacement error-handling defect. The A/B keys differ; no semantic-key collision is claimed. This is a follow-up to the cache introduced in #9266.
The independently reproduced COFF archive producer failure is tracked in #9439, with its fix in #9440. That failure can publish incorrect bytes on a cache miss; this issue concerns a false hit during restoration.