epoll: edge-triggered descriptors lost their edges on more than one CPU

EPOLLET reported a descriptor again only after a scan had seen it not
ready. Xorg watches its clients edge-triggered; when it read part of a
client's requests and more arrived before its next scan, the socket never
looked not-ready, so it was never reported again -- X waiting on the
client, the client waiting on X. One CPU almost never hit it; on a 16-thread
laptop the desktop froze within seconds: Window Maker's menu stuck, xterm
never mapped, text gone, and Caps Lock dead because X owns the LED.

Now a still-ready descriptor is reported again whenever anything has
signalled readiness since its last report (poll_generation()), which may be
spurious, as EPOLLET permits, and is never missed. Reproduced and verified
with QEMU at -smp 8: the root menu works and xterm runs cmatrix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-25 11:59:12 +01:00
commit 4743df8c4a
3 changed files with 25 additions and 2 deletions

View file

@ -47,6 +47,14 @@ poll_kick(void)
wakeup(&ticks);
}
// How many kicks there have been. epoll's edge-triggered mode compares it:
// see ep_ready().
uint
poll_generation(void)
{
return __atomic_load_n(&poll_seq, __ATOMIC_ACQUIRE);
}
// Sleep one timer tick, or until a poll_kick, or until killed/signaled.
// Returns 1 if a tick went by, 0 if it was a kick -- which must not count
// against a timeout, or a timed poll on a busy network would end early --