Signals are just a software interrupt mechanism, doubling as a way to kill runaway processes. All the things you are complaining about are problems with interrupts in general. Yet all computers since the 1960s still have interrupts, because they allow you to do things you can't do without them. And that's still true when you're talking about signals. Signals allow you, for example, to implement preemptive multithreading (with SIGALARM) or transparent transactional persistence (with SIGSEGV) or buffer overrun checking (ElectricFence, with SIGSEGV) in a userspace library.
However, they've clearly been expanded far beyond what they are necessary or even good for, and lamentably are still not usable for the primary use of hardware interrupts: I/O.
I must admit it is not trivial to replace this system with another equivalent without changing a lot of how Unix works. For instance signals are used in order to interrupt a program: if you don't make this trappable (like kill -9) then programs can't recover or prepare in any way before quitting. An alternative can be to just allow a signal handler to fire that will never return.
From this point of view you could even just have two signals, with the only goal of stopping processes, one in an hard way (SIGKILL) and one in a soft way (SIGINT), firing a signal handler (but the process will terminate when the handler returns). This way you don't need handling the interruption of syscalls nor writing "safe" signal handlers. As you said signals are interrupts. If the interrupt handler can never return a lot of problems disappeared.
All the other tasks currently performed with signals should use a more general and saner message bus, with select(2)able file-alike interface.
This is a more flexible system than Unix integer signals, but in practice the extra flexibility isn't often used, as a process can simply mount a ctl file into the namespace or post a pipe in /srv.
Reading your links now.
Edit: Yeah, that's pretty much what I was envisioning. I need to play with plan9 more:-\
* 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
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]).
[1] http://www.mail-archive.com/freebsd-net@freebsd.org/msg01788...
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
Note also that signalfd() can do something similar to your dispatch_source.