dschatz wrote:
Combuster wrote:
You'd have to go to quite some lengths to get a compiler to convert all I/O calls to continuations (or run a vm under the same merit), but supporting legacy is not strictly impossible.
Then again, getting rid of some legacy can be a good thing.
I don't think it's sufficient for a compiler to convert all I/O calls. It also needs to convert all function calls in a call stack that may block. Basically, turning blocking code into non-blocking code requires duplicating the entire call chain around the block. This is sometimes called "stack-ripping". Using a VM is feasible but makes it difficult to compose legacy code with new code (e.g. use existing libraries). In general, though, I agree, getting rid of some legacy is good, but my own philosophy is that a successful approach will be incremental, not reboot_the_world().
I've given some thought how to port GCC to such a system. GCC has blocking operations - when you start thinking about it even printf() and read() must be asynchronous - so to support it you have to be able to block.
You basically invert the common paradigm of implementing tasks on a thread pool - you implement threads as tasks that have their own stack ownership and that manually switch back to the regular stack when suspending, and have a continuation attached that switches back & continues the thread. Then you have a thread that is implemented as a sequence of tasks. You also make wrappers for all asynchronous functions that are synchronous and that link to the thread suspension mechanism - basically, make async call, attach continuation for resume, return.
You do a similar thing for new applications - you replace main() with a task entry point task_main() which takes a shared_ptr to the application as argument (or equivalent in whatever your language is). When that application object is deallocated everything is cleaned up for that application instance. That allows you to have literally zero threads in most applications.