Level-Triggered vs Edge-Triggered Notification Explained - embeddedpathashala.com
Skip to content
Menu
Close Menu
Level-Triggered vs Edge-Triggered Notification
Chapter 63 · epoll Notification Modes · EmbeddedPathashala
LT
Level-Triggered (default)
ET
Edge-Triggered (EPOLLET)
1 flag
EPOLLET to switch modes
epoll supports two completely different ways of telling your application that I/O is available: level-triggered (LT) and edge-triggered (ET) . This is one of the most important distinctions in Linux I/O programming. Choosing the wrong mode — or not fully understanding how ET works — leads to bugs where your server silently stops reading data and hangs forever.
This tutorial explains both modes clearly, step by step, with real code showing exactly what happens in each mode.
Key Concepts
Level-Triggered Edge-Triggered EPOLLET EPOLLIN epoll_wait() Signal-Driven I/O EAGAIN Nonblocking I/O EWOULDBLOCK
Level-Triggered Notification (Default)
Level-triggered is the default mode for epoll. It is also how poll() and select() work. The rule is simple:
Level-Triggered Rule: epoll_wait() reports a file descriptor as ready as long as data is available to read (or space is available to write). Every call to epoll_wait() will keep telling you the fd is ready until you drain the data.
Think of it like a water sensor. As long as there is water in the tank, the sensor stays ON. It doesn’t matter when the water arrived — if water is still there, the sensor triggers.
Level-Triggered Timeline
Event<br>① Data arrives on socket<br>② epoll_wait() #1 called<br>③ epoll_wait() #2 called (no new data)
LT result<br>Data in buffer<br>Returns fd as READY ✓<br>Returns fd as READY again ✓
As long as data sits unread in the buffer, every epoll_wait() keeps returning that fd as ready.
Benefit: You can use blocking file descriptors and read partial data. The kernel keeps reminding you until you finish reading. This is safe and easier to program.
Edge-Triggered Notification (EPOLLET)
Edge-triggered mode works very differently. The rule here is:
Edge-Triggered Rule: epoll_wait() reports a file descriptor as ready only when a new I/O event occurs since the previous epoll_wait(). If data arrived and you did not read it all, epoll_wait() will NOT notify you again — unless more data arrives.
Think of it like a doorbell. The bell rings when someone presses the button. If you ignore it and no one presses again, the bell won’t ring again on its own — even if the person is still standing at the door.
Edge-Triggered Timeline
Event<br>① Data arrives on socket<br>② epoll_wait() #1 called<br>③ epoll_wait() #2 called (no new data)
ET result<br>New I/O event recorded<br>Returns fd as READY ✓<br>BLOCKS — no new data ✗
If you read only part of the data on the first ready notification, the rest is silently stuck in the buffer with no more notifications.
This means with edge-triggered mode, when you get a ready notification, you must read all available data immediately — keep reading until the system call returns EAGAIN or EWOULDBLOCK. That’s the signal that the buffer is empty.
This is why edge-triggered epoll requires nonblocking file descriptors . If you use a blocking fd and try to read past the available data, your process would block forever.
LT vs ET — Side by Side
Level-Triggered (LT) — Default
✓ Simpler to program
✓ Works with blocking fds
✓ Partial reads are safe — kernel reminds you
✓ Same semantics as poll()/select()
Best for: most server applications, general use
Edge-Triggered (ET) — EPOLLET
✓ Slightly more efficient (fewer epoll_wait returns)
✓ Similar to signal-driven I/O semantics
⚠ Requires nonblocking file descriptors
⚠ Must drain all data on each notification
Best for: high-performance servers (nginx, etc.)
How to Enable Edge-Triggered Mode
To switch a specific fd to edge-triggered mode, just add the EPOLLET flag when calling epoll_ctl(). You can mix LT and ET file descriptors in the same epoll instance.
#include
struct epoll_event ev;
/* Register fd in EDGE-TRIGGERED mode */<br>ev.data.fd = fd;<br>ev.events = EPOLLIN | EPOLLET; /*<br>Important: The EPOLLET flag is per file descriptor, not global. You can have some fds in LT mode and others in ET mode in the same epoll instance. The listening socket is often kept in LT mode while client sockets use ET mode.
Step-by-Step Scenario: The Critical Difference
Let’s trace exactly what happens in both modes with a concrete scenario: 100 bytes arrive on a socket, but you only read 50 bytes on the first notification.
Scenario: 100 bytes arrive, you read only 50
Step 1: 100 bytes arrive on socket
→ Buffer now has 100 bytes
Step 2: epoll_wait() #1
Both LT and ET: fd returned as READY
Step 3: You read 50 bytes
→ Buffer still has 50 bytes remaining
Step 4: epoll_wait() #2
LT: fd returned as READY again ✓
50 bytes still in buffer — level triggered
ET: BLOCKS — no new I/O event ✗
50 bytes stuck — no notification until new data arrives
This is the silent bug in edge-triggered mode. If...