I've been reading parts of https://eel.is/c++draft/exec and other related articles regarding this topic. I know what schedulers, receivers, and senders are. However, I don't see how these interactions create such a new model for asynchronous programming in C++.
I'm curious how they differ from other models such as primitive std::future and std::promise, executors, and Boost's ASIO?
I'm curious how they differ from other models such as primitive
std::futureandstd::promise, executors, and Boost's ASIO?
Not sure what you mean by "executors" if not the very proposal you refer to, nor I know about Boost ASIO, but in regards to how P2300 (the proposal at the basis of that part of the draft you refer to) relates to std::future and std::promise, the difference is precisely that those two are very low level building blocks of what Eric Niebler calls "unstructured concurrency". With those building blocks, a lot of responsibility falls on you, as a programmer, in terms of managing lifetimes (of states, callbacks, ...).
P2300 is the basis of structured concurrency, which aims to provide building blocks that free you from (some of?) those responsiblities. To clarify, there's an analogy that Eric Niebler draws between
goto for control flow) vs structured programming (with for/while/if for control flow),std::future, std::promise, std::mutex, ...) vs structured concurrency (when_all, when_any, and other abstractions proposed in P2300, plus coroutines).This is a very broad topic, however, and I am honestly not able to answer thoroughly to the questions (if I could, I'd be probably be concerned more about how can I spend my 500k£ a year than spending time here), but I think you can find most of the answer to your question in these 3 talks (and at the links above):
Eric Niebler (and in the first video Daisy Hollman) will cover enough of the topic. Too much to put in an answer here, though. Plus it's not that I've understood every single bit of it :D
Eric explained it in this talk https://youtu.be/h-ExnuD6jms?t=525
Basically std::experimental::future<T>::then is slow and broken.
std::promise/std::future allocates and you have to do reference counting for this allocation.
There's a race condition between std::promise<R>::set_value
and future::then. To avoid the race condition you need std::mutex and std::condition_variable.
At the creation time of the
future/promise, you don't know the type of the continuation/callable. It must be type erased i.e.std::functionwhich has another allocation and maybe an indirection through a function pointer. So in summary, one or two allocations, an indirect function call, locking, reference counting. This is really heavy weight.
Senders and receivers do all this at compile time enabling std::execution::then to be lock and allocation free.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With