How to use epoll? A complete example in C
banu.com
banu.com
Don't write your own event loop for production code; write it to learn how they work.
(Also: uv? Really? That's a pretty bloated library that does everything from handling timers to figuring out the 15-minute load average on OpenBSD. If you just want timers and fds, use something simpler. select is actually fine for a large majority of applications.)
Libevent and libev are fine solutions to this problem. And I don't think you need to learn how to use epoll or kqueue to make use of them.
The critical thing to learn about epoll is event-triggering and level-triggering. I've seen people write libev code where they make watchers for r and w on a socket, then keep those alive regardless of whether or not they have data to write. This means their process never goes to sleep, since the fd is always writable and the write callback is called every time the process tries to go to sleep. They are expecting edge-triggering when ev provides level-triggering.
On the other hand, I've seen people using libzmq with libev have the opposite problem. libev's watchers are level-triggered, but libzmq's ZMQ_FDs are edge-triggered. This means you really have to know what you're doing to get things to work (but if you treat the ZMQ_FD like you should treat a normal fd, that is, read until EWOULDBLOCK and write until EWOULDBLOCK, it works), and reading an article that discusses how both work could be helpful in getting you to write correct code. OTOH, the terms themselves are pretty clear.
But I'm just saying that timers actually take a little bit of data structure work to get right; when it takes chin-ups just to get basic timers, code tends to have crappy (inefficient, slow) timing. Any good event library should give you the ability to schedule thousands and thousands of fine-grained timers without worrying about storage or the amount of time it takes to find the next wait interval.
the kernel has it's own version of printf, because it naturally can't run on the one that is in your userspace libc. I don't think that kprintf is exposed through the system call interface, though.
I'm see the point of timerfd(2), don't get me wrong. I'm just saying: a performant timer implementation only needs to tell the kernel one thing: when the next timer fires. Unlike poll(2) and select(2), which have O(n) bottlenecks in the u/k interface, timers are performant in pure userland code.
gettimeofday(2), on the other hand... there's a problem.
If so, that approach is very limited.
It cannot be used in a multithreaded environment effectively, because if you want to cancel or change the time of the next timeout while select/poll is blocking, you will have to somehow wake the polling thread to inform it of the change, adding overhead and complexity. Imagine a select/poll call for every TCP socket you have and a timeout for each one of them.
select/poll interface is also quite limited, you can't e.g. tell it which clock to use. I frequently work in a soft real time environment and I need to rely on high precision timing like CLOCK_MONOTONIC.
Also, select and poll have other problems, which is why epoll and kqueue were created in the first place.
I'm at a disadvantage arguing about working with evented timers in multithreaded environments, because generally I event to avoid threads. In environments where I've done both (for instance, Cocoa), I dedicate a thread to the event loop and give it a message queue interface to the other threads.
"Waking the polling thread" is pretty straightforward. Just set up a pipe and ping it when you have a message you need delivered immediately. That's no more expensive than "timerfd".
I'm not arguing that select/poll are superior to epoll and kqueue (although select is just fine for many applications; why would I care, though; I don't write directly to select or kqueue). I'm saying that the Unix interface to timers isn't unscalable, and the scalability problems are in userland. Unlike select/poll, which did in fact have an O(n) problem in the u/k interface, the Unix interface to timers isn't a bottleneck.
http://leaf.dragonflybsd.org/cgi/web-man?command=kprintf&...
Or better yet, program in Go ;)
I have no argument in principle for the idea that you should restructure your whole program's control flow with transparent cooperative scheduling to attempt to get the best of both worlds of straight-line coding and efficient scheduling, except that it feels to me like you're kind of not really even writing C code anymore at that point. This might be irrational of me.
In the meantime, if you're not going to adopt an exotic thread scheduling library, I think events are the way to go. POSIX threads don't make much sense to me.
Maintaining any kind of long-term state for the functions which the event loop calls can be trickier than it needs to be, since they need to quickly return and can't leave anything on their stack.
GHC's thread system and IO manager is implemented in terms of epoll, for example.
And when I write performance-oriented C code, I use events, because great tight resource control is facilitated by that style.