D674 - A high-water mark that could move backwards

Status: decided Date: 2026-09-10

Found by not dismissing a flake

./bin/orbistoun check failed on cargo test --workspace twice, on two different turns, while every direct re-run of the same command passed. The first time I recorded it as "couldn't reproduce, flagging rather than claiming it's nothing". The second time was the signal.

There was no captured output either time - my own tail -6 on the gate had thrown the failure detail away, which is worth noting on its own: a pipe that trims a gate's output trims the part you need.

So it was found by reading rather than by reproducing.

The race

orbistoun-input recognises a handle it issued by comparing against a high-water mark:

static ISSUED: AtomicU32 = AtomicU32::new(0);
ISSUED.store(handle.as_raw(), Relaxed);   // in scePadOpen, and in sceMouseOpen
fn ours(raw: u64) -> Option<Handle> { … (raw <= ISSUED.load(Relaxed)).then_some(handle) }

The allocator is behind a Mutex; the mark is not. Two threads opening at once:

thread A thread B ISSUED
1 allocates 4 0
2 allocates 5 0
3 stores 5 5
4 stores 4 4

Handle 5 is live, above the mark, and refused by every call that takes it - scePadReadState answers no such pad for a pad the guest just opened.

fetch_max instead of store. A mark that only rises cannot disagree with an allocator that only counts up.

Not only a test problem

The tests run in parallel, which is what made it visible - but this is the guest-facing path. Two guest threads opening pads at once hit exactly the same window, and the failure there is a title told its controller does not exist, intermittently, with nothing in the trace to say why.

Tested as a property, because the interleaving cannot be

A test that spawns threads and hopes to catch the window is probabilistic, which is a poor guard: it passes on a good day and teaches nothing. The property is deterministic and is the whole of the defect - recording a handle must never lower the mark - so remember is a function of its own and the test records out of order and asserts the higher handle is still recognised.

Watched to fail: reverting fetch_max to store reds it with the message it was written for. Both copies have it, because both crates carry their own mark and a fix in one is not a fix in the other.