Async.h – asynchronous, stackless subroutines in C
higherlogics.blogspot.com
higherlogics.blogspot.com
- "Protothreads" are just the old switch statement coroutine trick. Local variables do not persist across async switches - this is expected and not surprising but that doesn't make it any less unnerving, it would be very easy to accidentally introduce very hard-to-debug problems in code using this technique. None of the author's examples expose this limitation!
- async.h seems to be just the addition of a per-protothread structure where you can store your state (where the old coroutine trick typically has people just using static variables). This improves things a little but the author's examples still don't highlight the major issue that local variables don't persist. I suspect they didn't mention it because it seems so obvious, but that doesn't mean it isn't important.
I worked for about 2 years on code that's perfectly suited to these techniques (i.e. software that manages inherently parallel procedures, but that cant use actual threads), spent lots of time considering them, but in the end I think it's just better to manually manage your flow control. You end up with state coming in and out of functions as struct members (which is exactly the main idea of async.h IIUC) and the flow of your code doesn't look very naturally like the flow of the procedures it's actually managing. The solution IMO is just to code carefully, write comments, and draw diagrams.
My C files like this start with a couple pages of prose outlining the operation of the HW they're interacting with (if it was not interacting with HW, it would probably describe the protocol it's implementing, or the user interaction it's facilitating), and how that relates to the design of the SW in the file. Once you have that, I think the reader can see how the slightly unnatural function boundaries map onto elements of the broader system.
Sorry to be negative, these headers are cool and interesting and I'm glad people invent them. I just don't think we should use them in important software.
But in my opinion I would not use them for generic use cases and rather wait for the official support in C++20.
Of course I'm aware that I'm talking about C++ here and the situation for C might be slightly different.
You get block statements just with Clang don't you? No need for libdispatch?
And you can kinda get blocks with GCC as well using a similar construct. (For the love of all that is holy, don't actually use this macro).
#define lambda(ret_type, _body) ({ ret_type _ _body _; })
I don't quite remember, but from when I was working things out, this worked _most_ of the time for both GCC and clang.That's great!
Or, you know, when you happen to want to declare a few local variables.
Does anyone have experience using libunwind for this kind of thing? You need to save and swap state for actual parallel execution.
Async/await semantically doesn't support resumption from deep in the stack, so it's a good choice for this sort of trick.
I don't see myself using this in any kind of real code at this stage, although it might serve as a base for a more comprehensive library of Rust-style futures.
1. not portable C
2. requires more storage for each continuation (4 bytes vs. 2)
> this is a header-only async/await implementation for C based on Duff's device