the event loop
Picture a single receptionist at a busy clinic with no other staff. Rather than sit one-on-one with each patient, the receptionist waits at the desk until SOMETHING happens - a new arrival, a phone ringing, a lab result coming back - handles that one thing quickly, then immediately waits for the next. This wait-then-handle-then-wait cycle is an event loop, and it is how one thread can keep thousands of network connections moving without a thread per connection.
Concretely, an event loop is a tight cycle wrapped around a readiness call. Step one: block in epoll_wait() (or poll, or kqueue) until one or more descriptors are ready. Step two: for each ready descriptor, run a short, non-blocking handler - accept a new connection, read available bytes, write what the kernel will take - and then return to the loop. Step three: go back and wait again. The iron rule is that no handler may block: every handler does a small, bounded amount of work on non-blocking descriptors and hands control straight back, because while the single loop thread is stuck, EVERY connection it serves is frozen. Timers and queued callbacks are folded in by computing the wait timeout from the nearest pending timer.
Why it matters: the event loop is the runtime heart of nginx, Redis, Node.js, libuv, and most async frameworks. It converts 'many concurrent connections' into 'many tiny callbacks dispatched from one waiting point,' trading the simplicity of blocking code for enormous scalability. Its great weakness is the same as its rule: one slow or blocking handler - a synchronous file read, a heavy CPU loop, a blocking DNS call - stalls the whole loop and every connection with it, which is why such work is pushed to thread pools or made asynchronous.
for (;;) { int n = epoll_wait(ep, evs, MAX, next_timer_ms()); for (int i = 0; i < n; i++) handle_ready(evs[i].data.ptr); run_due_timers(); }
Wait for readiness, dispatch a short non-blocking handler for each ready event, fire due timers, repeat.
An event loop gives concurrency on one thread, not parallelism: handlers run one at a time, so CPU-heavy work must be moved off the loop or it blocks every connection. This is a different layer from the task scheduler / async executor that decides which suspended task to resume - the loop just waits and dispatches.