Wait for room in the flight table in mem_lazy_skip only when there is another buffer's range to give back.

The review found that memory.c mem_lazy_skip's loop over the ranges armed for another buffer waited for a free entry
in the flight table before it looked for one. On its last pass, which finds nothing left to take, the replay could
still wait for another thread's fetch to end when the table was full. Now it looks first, and waits for room only when
it has a range to take and every entry is in use, then looks again. The search is lazy_foreign_locked, shared with the
check before the loop. Not reproduced before the change; lazy_fetch_check passed in all its scenarios (foreign and
flood among them).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-13 20:07:14 -05:00
parent 47078fa0cd
commit 61b8a6d62c

View File

@@ -630,6 +630,14 @@ uint64_t mem_lazy_arm(uint64_t a, uint64_t n, uint64_t h0, uint64_t h1, uint64_t
return armed;
}
// lz_lock held: the last armed segment that meets [a, a + n) and was armed for another buffer or offset than h0 and
// delta, -1 none (mem_lazy_skip).
static int lazy_foreign_locked(uint64_t a, uint64_t n, uint64_t h0, int64_t delta) {
int i = lz_n;
while (i-- > 0 && !(lzs[i].lo < a + n && lzs[i].hi > a && (lzs[i].h0 != h0 || lzs[i].delta != delta))) {}
return i;
}
// The armed parts of [a, a + n), sorted, into out (at most max); they count as stale from here on: the caller leaves
// the bytes it is about to write there in the mirror h0, at buffer offset address + delta. Returns how many.
// A segment there that was armed for another buffer (a mirror evicted since, whose range a write-back armed while the
@@ -644,18 +652,18 @@ int mem_lazy_skip(uint64_t a, uint64_t n, uint64_t h0, int64_t delta, uint64_t o
// A segment armed for another buffer goes back under frz_lock, through flight without a fetch (stale 0: its pages
// only), with arms held off until the rest is marked, as before lz_lock; that is rare, and the replay's per-draw
// check holds lz_lock only.
int foreign = 0;
plat_mutex_lock(&lz_lock);
for (int i = 0; i < lz_n && !foreign; i++)
foreign = lzs[i].lo < a + n && lzs[i].hi > a && (lzs[i].h0 != h0 || lzs[i].delta != delta);
int foreign = lazy_foreign_locked(a, n, h0, delta) >= 0;
if (foreign) {
plat_mutex_unlock(&lz_lock);
plat_mutex_lock(&frz_lock);
for (;;) {
plat_mutex_lock(&lz_lock);
lazy_fly_room_locked();
int i = lz_n, f = -1;
while (i-- > 0 && !(lzs[i].lo < a + n && lzs[i].hi > a && (lzs[i].h0 != h0 || lzs[i].delta != delta))) {}
int i = lazy_foreign_locked(a, n, h0, delta), f = -1;
if (i >= 0 && lz_nfly >= LZ_FLY) { // one to take and every entry in use: wait for one, then look again
lazy_fly_room_locked();
i = lazy_foreign_locked(a, n, h0, delta);
}
LzSeg s;
if (i >= 0) {
lzs[i].stale = 0;