What is the minimum set of actions I should do to use C++ co_await
operator like C# await
operator?
There is an article on cppreferense about corutines, where there is C# like class task<>
which is used as return type, but I can't find it in standard library. When function return void, this predictably leads to a compile error.
task
in that case is just a placeholder for the plumbing you put together yourself.From https://en.cppreference.com/w/cpp/language/coroutines
When you use
co_*
in your code you need to#include <coroutine>
. The compiler will do stuff to your function. Example:The above code results in the following error:
Whatever the compiler is doing, it is looking for a
promise_type
but it cannot find it.To get past this we need to introduce the following code.
The return type of the
csp_await
function is changed to use ourtask
type which results in a different error:So we add some more stuff
The next thing to do is to make the
send
function of thebidirectional_channel
coroutine friendly. It is currently a blocking function with the return typevoid
which cannot be used as is.send_async_awaiter
is our awaitable, coroutine friendly type.send_async
is just what thesend
function would need to be changed into to be awaitable. This code compiles but it doesn't do anything.I don't want to make this about CSP but I need to go into a little bit of detail about what needs to happen in the channel send function. In this example I'm using an unbuffered channel which should suspend until there is a ready receiver. Assuming the receiver isn't ready to begin with we need to park the send operation somewhere. To do this we need to only consider
await_ready
andawait_suspend
.await_ready
is returning false, so the next thing to happen will beawait_suspend
. we get a generic handle to our coroutine that we can use to park it.The implementation of
bidirectional_channel
that I started out with is based on threads. It uses a mutex and conditional variables to wake up blocked threads but I don't think abidirectional_channel_async
type should be implemented in the same way. Instead, I'm going to consider an invasive approach where the suspended coroutine is parked inside the channel. I think this makes sense because this is where I later need to be able to find a parked coroutine to resume when the receiver is ready. It also implies that we could write a completely single threaded CSP library that use cooperative instead of preemptive multithreading. I think adding parallelism to this async CSP library would be easier than going the other way.I didn't go into all the details of everything here but it's a start. I'm kinda glad that it is decoupled like this because you can build whatever you think is best on top of this.