Is there interest for a coroutine-based library to make asynchronous APIs easier to deal with?
Typically, for each task there is cruft: chaining callbacks, managing intermediate state and error codes (since exceptions don't fit this model). The code flow get inverted and becomes difficult to follow.
Recent versions of F# and C# solve this problem. They implement an await operator that effectively suspends the executing method until a task completes. The compiler takes care of transforming subsequent code into a continuation. Everything runs on the main thread, with asynchronous methods spending most time awaiting. N3328 proposes resumable functions of this kind in C++.
For an immediate solution we could leverage the Boost.Context/Coroutine library. The resulting code may look like this:
try { task = do_a_async(...)
// yield until task done task.await(); } catch (const some_exception& e) { // exceptions arrive in awaiting context }
// normal code flow for (auto& task : tasks1) { task.await(); }
taskAny = await_any(tasks2); taskAny.await();
...
FWIW, HPX provides all this and more (https://github.com/STEllAR-GROUP/hpx/). It's well aligned with the Standard's semantics and exposes an interface very close to what you're showing above. As a bonus all of this is available in distributed scenarios as well (remote thread scheduling and synchronization).
There needs to be a representation for Awaitable tasks (similar to std::future but non-blocking). The other requirement is to have a Scheduler (run loop) in order to weave between coroutines.
We strongly believe that we don't need a new construct for this. Threads and
futures is exactly the abstraction to be used for this as well. In HPX,
hpx::thread (full semantic equivalence to std::thread, except it represents
a task) and hpx::future expose semantics similar to those you're describing:
hpx::future<int> f = hpx::async([](){ return 42; });
BOOST_ASSERT(f.get() == 42);
Additionally HPX implements N3558 (A Standardized Representation of
Asynchronous Operations,
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3558.pdf), allowing
to write:
hpx::future<int> f1 = hpx::async([](){ return 42; });
hpx::future<int> f2 = hpx::async([](){ return 43; });
tuple
Benefits: - normal code flow: plain conditionals, loops, exceptions, RAII - algorithm state tracked on coroutine stack - async tasks are composable - any async API can be wrapped
Cons: - must wrap async APIs (e.g. Boost.Asio) - needs std::exception_ptr to dispatch exceptions - stackful coroutines are sometimes difficult to debug
I wrote an open-source library that does this: https://github.com/vmilea/CppAwait.
It's far from Boost style but the concept looks sane. For a comparison between async patterns please see: https://github.com/vmilea/CppAwait/blob/master/Examples/ex_stockClient.cpp
Making a Boost version would involve serious redesign. So is this worth pursuing?