gex native: futex waits read the clock once per deadline check

A wait checked start.elapsed() against its deadline and then subtracted a
second, later reading from it; when the deadline passed in between, the host
panicked ("overflow when subtracting durations"). Absolute monotonic
FUTEX_WAIT_BITSET deadlines, now short and real (02b4f7f7), hit it in Hollow
Knight.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_461fe3ff06374ecba693831148697ba6
This commit is contained in:
velzieandClaude Opus 5.5 committed 2026-10-06 23:21:04 -04:00
1 parent 02b4f7f709
commit 4a4510fbc2
1 file changed
+9 -6
+9 -6
View File
@@ -1406,9 +1406,12 @@ fn perform_block(
let start = std::time::Instant::now();
let slice = std::time::Duration::from_millis(20);
loop {
let left = match deadline {
Some(d) if start.elapsed() >= d => return Err(Errno::ETIMEDOUT),
Some(d) => (d - start.elapsed()).min(slice),
// One reading of the clock: checking it and then subtracting a
// second, later reading overflowed when the deadline passed
// in between.
let left = match deadline.map(|d| d.checked_sub(start.elapsed())) {
Some(None) | Some(Some(std::time::Duration::ZERO)) => return Err(Errno::ETIMEDOUT),
Some(Some(left)) => left.min(slice),
None => slice,
};
match table.wait(uaddr, val, Some(left), &|| mem.read_u32(uaddr).ok()) {
@@ -1447,9 +1450,9 @@ fn perform_block(
if v != want && gex_atomic::cas_u32(mem.bytes, uaddr, v, want).ok_or(Errno::EFAULT)?.is_err() {
continue;
}
let left = match deadline {
Some(d) if start.elapsed() >= d => return Err(Errno::ETIMEDOUT),
Some(d) => d - start.elapsed(),
let left = match deadline.map(|d| d.checked_sub(start.elapsed())) {
Some(None) | Some(Some(std::time::Duration::ZERO)) => return Err(Errno::ETIMEDOUT),
Some(Some(left)) => left,
None => std::time::Duration::from_millis(100),
};
// In slices: the owner's UNLOCK_PI wakes this, and the word