Create Your Own Event Queue 78 10000000000000000000000000000001 read_event? true, edge_triggered: true, oneshot?: false The next topic we will introduce in this chapter is the concept of edge-triggered events, which probably need some explanation. Level-triggered versus edge-triggered events In a perfect world, we wouldn't need to discuss this, but when working with epoll, it's almost impossible to avoid having to know about the difference. It's not obvious by reading the documentation, especially not if you haven't had previous experience with these terms before. The interesting part of this is that it allows us to create a parallel between how events are handled in epoll and how events are handled at the hardware level. epoll can notify events in a level-triggered or edge-triggered mode. If your main experience is programming in high-level languages, this must sound very obscure (it did to me when I first learned about it), but bear with me. In the events bitmask on the Event struct, we set the EPOLLET flag to get notified in edge-triggered mode (the default if you specify nothing is level-triggered). This way of modeling event notification and event handling has a lot of similarities to how computers handle interrupts. Level-triggered means that the answer to the question “Has an event happened” is true as long as the electrical signal on an interrupt line is reported as high. If we translate this to our example, a read event has occurred as long as there is data in the buffer associated with the file handle. When handling interrupts, you would clear the interrupt by servicing whatever hardware caused it, or you could mask the interrupt, which simply disables interrupts on that line until it's explicitly unmasked later on. In our example, we clear the interrupt by draining all the data in the buffer by reading it. When the buffer is drained, the answer to our question changes to false. When using epoll in its default mode, which is level-triggered, we can encounter a case where we get multiple notifications on the same event since we haven't had time to drain the buffer yet (remember, as long as there is data in the buffer, epoll will notify you over and over again). This is especially apparent when we have one thread that reports events and then delegates the task of handling the event (reading from the stream) to other worker threads since epoll will happily report that an event is ready even though we're in the process of handling it. To remedy this, epoll has a flag named EPOLLONESHOT.