Check that an arm keeps its region marked while a range in flight there ends, which the review found untested.

memory.c mem_lazy_arm marks the region it protects in lz_arm_r, so that lazy_flags_locked keeps lzr set when a
release's flight in the same region ends during the arm's clean test. Without that mark the flight's end cleared lzr,
lazy_fault let a read of the arming range go, and access_fault passed it to the crash handler. All 20 scenarios of
tests/lazy_fetch_check.py passed with the mark taken out. The new arm_flight scenario has a release fetch one range for
100 ms, an arm of another range of the region wait 400 ms in its clean test, and a read of that range fault after the
flight ended: it passes, and exits with an access violation when lazy_flags_locked ignores lz_arm_r.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-13 19:59:27 -05:00
parent 2e077edc9d
commit 92121a11e8
3 changed files with 26 additions and 1 deletions

View File

@@ -692,7 +692,8 @@ A change to the lazy write-back's arms, faults, releases or fetches (`runtime/me
in flight", `runtime/vk_memory.c` `lazy_fetch`, `mirror_evict`, `hot_check`) passes `python tests\lazy_fetch_check.py`,
which builds memory.c with the Windows fault handler and a fetch that only waits, and runs each scenario as its own
process, so an access violation fails it. A thread that reads a range over and over while it is armed and given back
again never reaches the crash handler, also when the arm's clean test takes 100 ms. A fetch holds up nothing but the
again never reaches the crash handler, also when the arm's clean test takes 100 ms, and a read of a range being armed
does not either when a range in flight in its region ends during that test. A fetch holds up nothing but the
touches of its range: freezes, thaws, arms, releases and copies of other ranges return at once meanwhile, two ranges
fetch at once, and more touching threads than the flight table holds all end. The touches of a range in flight wait
for it and run again, a write among them without releasing the region's other ranges; thaws, mapping changes,

View File

@@ -152,6 +152,9 @@ static void releaser(void *arg) {
mem_lazy_release((const uint64_t (*)[3])rel_rg, 1);
__atomic_store_n(&rel_done, 1, __ATOMIC_SEQ_CST);
}
// the completion thread's arm (vk_memory.c vk_gpu_written_back) of 64 KiB at arg, written back from buffer 1
static volatile uint64_t arm_n;
static void armer(void *arg) { arm_n = arm_buf((uint64_t)(uintptr_t)arg, 0x10000, 1); }
int main(int argc, char **argv) {
setvbuf(stdout, NULL, _IONBF, 0);
@@ -360,6 +363,26 @@ int main(int argc, char **argv) {
plat_thread_join(tr);
CHECK(page_rw(s) && !lzr[r], "the range in flight was not given back");
CHECK(arm_buf(s, K64, 1) == K64 && armed(s, K64), "the range was not armed again after its flight");
} else if (!strcmp(what, "arm_flight")) {
// A range in flight (a release, so no hold) in region r ends while an arm of another range in r is in its
// clean test: the region keeps its mark (memory.c lz_arm_r), so a read of the arming range that faults after
// that end waits for the arm instead of reaching the crash handler.
uint64_t R = heap + 120 * M, s1 = R, s2 = R + M;
CHECK(arm_buf(s1, K64, 1) == K64 && stale(s1, K64, 1) == 1, "arm s1");
fetch_ns = 100000000ull;
rel_rg[0][0] = s1, rel_rg[0][1] = s1 + K64, rel_rg[0][2] = 0;
void *tr = plat_thread_new(releaser, NULL, "releaser");
wait_fetch();
clean_ns = 400000000ull;
void *tc = plat_thread_new(armer, (void *)(uintptr_t)s2, "armer");
while (!__atomic_load_n(&rel_done, __ATOMIC_SEQ_CST)) plat_wait_ns(1000000);
plat_wait_ns(30000000); // the flight has ended; the arm is in its clean test
Touch b;
void *tb = touch(&b, s2 + 0x10, 0);
plat_thread_join(tr);
plat_thread_join(tc);
plat_thread_join(tb);
CHECK(arm_n == K64 && b.done, "armed %llu, read done %d", (unsigned long long)arm_n, b.done);
} else if (!strcmp(what, "foreign")) {
// a write by the replay to a range armed for another buffer (a mirror gone since) gives it back without a
// fetch: that buffer may be gone

View File

@@ -33,6 +33,7 @@ SCENARIOS = {
'release': ('release', {}),
'release_join': ('release_join', {}),
'arm_busy': ('arm_busy', {}),
'arm_flight': ('arm_flight', {}),
'foreign': ('foreign', {}),
'chunk': ('chunk', CHUNK),
'locked': ('locked', LOCKED),