The real reason why true asynchronous file IO is hard
Yesterday I wrote about the obstacles that faced the Linux kernel in delivering true kernel-level asynchronous file IO. In that entry I wrote:
Revising all of this [synchronous filesystem] code to work asynchronously is a lot of work and so didn't get done for a while.
Well. That's the real reason in a nutshell. To wit, writing callback based non-blocking code in conventional languages, especially in C, is hard and a pain in the rear. It takes what is simple straight line code and contorts it massively. You wind up with a maze of callback functions, state tracking objects, and so on, and your actual logic and control flow vanishes into the underbrush never to be seen again. It should be no surprise that people avoid this like the plague; it's hard to write, hard to debug, and hard to understand.
In fact, the most successful abstraction for dealing with asynchronous IO is in fact the thread, which you use to make it synchronous again in your actual code. This is clearly visible in things like the Linux kernel, where the kernel-level IO is almost invariably asynchronous but most code immediately waits for its IO to complete.
All of this should not be surprising. Ordinary code contains a large amount of implicit state (in the form of the call stack, local variables, and the location of the program counter). With our current technologies, writing explicitly asynchronous code generally requires capturing and restoring this state explicitly, while threads manage the state for you implicitly. Having to do things explicitly instead of having them handled implicitly is always a pain.
(It doesn't help that this implicit state is so fundamental to how we think about code, since in the abstract code is the sequence of steps that get run through in some environment.)
This issue in general makes me feel that general asynchronous programming in current languages is not going to catch on and become popular, because all of them make it too hard. One way or another, asynchronous programming systems need to preserve the implicit state for you, so that you can write what looks like ordinary sequential code and it becomes asynchronous mostly behind your back. Probably it's too late for C, though; threads are likely to be it, more or less.
(There are languages that have done this, sometimes only in limited ways
such as Python's '