Level-triggered reports a descriptor as ready on every wait while unread data remains. Edge-triggered reports it once per change, so you must read until EAGAIN or you can stall. In kqueue the edge-like behaviour comes from EV_CLEAR.