A stack-less Rust coroutine library under 100 LoC
Posted January 25, 2020 ‐ 4 min read
As of stable
std and is
stack-less (meaning, not depending on a separate CPU architecture stack).
A very basic simple coroutine library contains only an event-less 'yield' primitive, which stops execution of the current coroutine so that other coroutines can run. This is the kind of library that I chose to demonstrate in this post to provide the most concise example.
To the coroutine we pass a
Fib struct that only contains a simple binary
Fib struct has a
waiter method that creates a
coroutine can use in order to be
use Future; use Pin; use ;
Our executor keeps a vector of uncompleted futures, where the state of each
future is located on the heap. As a very basic executor, it only supports
adding futures to it before actual execution takes place and not afterward.
push method adds a closure to the list of futures, and the
performs interlaced execution of futures until all of them complete.
For the executor implementation above, we need a null, similar to the one used in ( ).
use , const RAW_WAKER: RawWaker = new; const VTABLE: RawWakerVTable = new; unsafe unsafe unsafe unsafe
Giving it a go
We can test the library using a program such as the following:
The output is as follows:
Running 1 A 2 A 3 A 1 B 2 B 3 B 1 C 2 C 3 C 1 D 2 D 3 D Done
Timing the following program compiled with
lto = true, I have seen that it
takes about 5 nanoseconds for each iteration of the internal loop, on an Intel
One of the nice things about the
await support in the Rust compiler
is that it does not depend on any specific run-time. Thus, if you commit to
certain run-time, you are free to implement your own executor.
Independency of run-time has its downsides. For example, the library presented
here is not compatible with other run-times such as
poll function by assuming
Future will always be
Ready after it was
Combined uses of several run-times in a single program is possible but requires extra care (see).