An empty reading means unknown, never changed
scope: generic · severity: law · confidence: proven · subsystem: method
When you use a value as a baseline, a failed read must not be able to masquerade as a changed value.
The worked example. tk_boot_id reads
/proc/sys/kernel/random/boot_id to tell whether a device rebooted. A single
timed-out read returns empty — and an empty string compares unequal to every
real boot id. So a caller that took its baseline through one transient failure
sees “the device rebooted” on the very next read, and on every read after that,
forever.
That is exactly what happened: sshd was stalling ~7.9 s answering a trivial
command, the 6 s timeout ate the baseline, and a 20-cycle camera test printed
BOOT_ID CHANGED and FAIL against a phone that had not rebooted once.
Two things fix it, and you need both:
- Retry, so empty is rare.
tk_boot_idtries twice. - Put the timeout above the known stall, or retrying buys nothing. Two attempts at 12 s, not three at 6 s. Three attempts that each fail at 6 s against a 7.9 s stall is just a slower failure.
Cost when the device is genuinely gone is nothing: with the USB interface down ssh fails instantly, so the retry is free. The long worst case is only spent on a device that accepts the connection and then stalls — which is precisely the case worth waiting for.
Generalise: any sentinel that means “I could not read this” must be
distinguishable from every legal value. If your sentinel is "", 0, or -1,
check what a legal value can be.
Related: every-test-needs-a-positive-control, poll-never-sleep.
