The async/await idiom is becoming increasingly popular. The first widely used language to include it was C#, and it has now spread into JavaScript and Rust. Now C/C++ programmers don't have to feel left out, because async.h is a header-only library that brings async/await to C!
Features:
- It's 100% portable C.
- It requires very little state (2 bytes).
- It's not dependent on an OS.
- It's a bit simpler to understand than protothreads because the async state is caller-saved rather than callee-saved.
#include "async.h"
struct async pt;
struct timer timer;
async example(struct async *pt) {
async_begin(pt);
while(1) {
if(initiate_io()) {
timer_start(&timer);
await(io_completed() || timer_expired(&timer));
read_data();
}
}
async_end;
}
This library is basically a modified version of the idioms found in the Protothreads library by Adam Dunkels, so it's not truly ground breaking. I've made a few tweaks that make it more understandable and generate more compact code, and I also think it more cleanly maps to the async/await semantics than it does to true threading.
Protothreads and async.h are both based around local continuations, but where protothreads are callee-saved, async.h is caller-saved. This eliminates the need to pass in the local continuation to any async operations except async_begin
. This simplifies the macros that implement the async/await idiom, and even simplifies code that uses async.h.
Here's a simple example of fork-join style "parallelism":
#include "async.h"
typedef struct {
async_state;
struct async nested1;
struct async nested2;
} example_state;
example_state pt;
async nested(struct async *pt){
async_begin(pt);
...
async_end;
}
async example(example_state *pt) {
async_begin(pt);
// fork two nested async subroutines and wait until both complete
async_init(&pt->nested1);
async_init(&pt->nested2);
await(async_call(nested, &pt->nested1) & async_call(nested, &pt->nested2));
// fork two nested async subroutines and wait until at least one completes
async_init(&pt->nested1);
async_init(&pt->nested2);
await(async_call(nested, &pt->nested1) | async_call(nested, &pt->nested2));
async_end;
}
Comments