It is "safe" insofar as it won't crash, but unless you continue calling read
until you get EAGAIN
(or zero, which means the other end has closed the connection), you will sometimes make wrong assumptions about availability of data. What's worst is that it will most of the time look like it works fine, too.
Edge-triggered as opposed to level-triggered notification only guarantees that you get one notification if the readiness state changed since the last time you called epoll_wait
, even if there remains data that you could read.
Edge-triggered event notification does behave kind of weird or unintuitively under Linux sometimes, so it may do something different from what you expect and e.g. give you another notification when more data arrives (so your code appears to "work anyway") but that is not what's being guaranteed.
I've had similar "surprises" when using epoll
with eventfd
. What you'd expect to happen in edge-triggered mode would be all threads that are already blocked waking up (all at the same time, and exactly once), and everyone calling epoll_wait
after the event is signalled blocking until the event is consumed and signalled again. What it really does is wake the first thread that called epoll_wait
. And surprise again, level-triggered mode works exactly as you'd wish, except you must consume the event to be able to ready it again, for which there is no proper way of doing (as you must do it exactly once or you'll block in read
).
Thus, if you don't consume all data and later wait for being notified again, you may be lucky and it will "work anyway", or you may wait for a quite long time, possibly forever. My recommendation is therefore to definitely keep reading until you get EAGAIN
, it's the only truly reliable thing to avoid surprises.
Do note that you can starve slow senders if you keep naively reading. If you have a very fast sender and you keep reading on the fast sender then you'll never see EAGAIN
(at least not for as long as the other end keeps sending!), and you will completely starve other senders.
It therefore makes sense to put all ready descriptors in a list and read them round-robin, removing them from the list when they return EAGAIN
.