It's funny that you should bring that up... During this whole back-and-forth I can't help but think that Apple's libdispatch solves most of the issues brought up by both sides:
* Use a dispatch queue and dispatch_async a block to handle computation. Since the queues work from a shared thread pool and are managed at the kernel level, a long-running Fib calculation won't hold up other blocks from processing on any concurrent queue (it would still hold up a sequential queue...obviously).
* Use dispatch_sources to do your IO. Now you don't have to worry about clumsy callbacks, and your IO operations are perfectly happy co-existing with your long-running computations.
* Use dispatch_read/dispatch_write to persist to disk and you don't have to worry about different latency and throughput characteristics of files vs sockets
...and to the point about signals:
* Use a signal dispatch_source to do things in response to signals that you would never have thought possible. This is possible because libdispatch sets up a trampoline to catch the actual signal and then turn around and queue your dispatch_source block. It's not a complete panacea, but it's close...
Internally, libdispatch is built on a system call named "kevent" (part of FreeBSD, first introduced in 4.1, meaning it was introduced in 2000). This system call will block while waiting for an event.
When a system call blocks, it is subject to a signal interrupting it, in which case you need to check for the EINTR error condition to restart the system call manually, unless you have SA_RESTART set on your signal handler /and/ your system call is one of a small set of "standard I/O" calls (note: kevent is not on this list[1]).
Unfortunately, looking at libdispatch's source code, one will note that none of the usages of kevent() are correctly handling EINTR. I noticed this, for the record, while debugging the "dispatch_assume_zero(k_err)" that I had determined was being hit while running some (buggy) code of mine inside of libdispatch (it checks for EBADF, and that's it).
tl;dr signals are insidious and libdispatch didn't care ;P
Epoll is limited to file descriptors. Kqueue also supports waiting on process events (process exit, process fork, process exec, etc), on filesystem events, and a bunch of other things. Epoll also does not support batch updates and requires multiple system calls to apply updates for multiple file descriptors, while kqueue supports batch updating and polling in a single system call. All in all kqueue is just superior to epoll. See also http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod for various ways in which epoll is broken (search for "The epoll mechanism deserves honorable mention as the most misdesigned of the more advanced event mechanisms")
Not to mention that kevent contain the number of bytes to be read, or the number of connections to accept, which can avoid you a system call that returns EAGAIN in a non-blocking socket.
* Use a dispatch queue and dispatch_async a block to handle computation. Since the queues work from a shared thread pool and are managed at the kernel level, a long-running Fib calculation won't hold up other blocks from processing on any concurrent queue (it would still hold up a sequential queue...obviously).
* Use dispatch_sources to do your IO. Now you don't have to worry about clumsy callbacks, and your IO operations are perfectly happy co-existing with your long-running computations.
* Use dispatch_read/dispatch_write to persist to disk and you don't have to worry about different latency and throughput characteristics of files vs sockets
...and to the point about signals:
* Use a signal dispatch_source to do things in response to signals that you would never have thought possible. This is possible because libdispatch sets up a trampoline to catch the actual signal and then turn around and queue your dispatch_source block. It's not a complete panacea, but it's close...
...now if only Linux would implement kqueue