C++ futures part 1: The mental model
devblogs.microsoft.com
devblogs.microsoft.com
C++ async is unfortunately a "bring your own runtime" sort of model (for now), but it does handle the part that would be incredibly tedious by hand, building above mentioned state machine.
Heap allocation is not required, just the default.
You can do static allocation if you wish, though it is a little more complicated than typical static allocation due to the relative unpredictability.
So anyone that has experience in such model, including the awaitable magic methods, can grasp it.
While not cost free, the optimisers are expected to elide the cost most of the time.
Delegating the runtime nerfs many of the benefits that async can offer. If you are going to delegate the runtime it offers relatively little compared to just using threads.
C++ provides quite a broad set of features for concurrency support (https://cppreference.com/cpp/thread). The task-based paradigm (i.e. promise/future etc.) is a logical framework which is rather confusing to folks who come from a straight thread-based (think pthreads) background.
Raymond Chen in his own inimitable style makes things clear so one can see that the logical task-based framework is nothing more than a higher level abstraction above thread-based core routines i.e. there is no underlying magic involved.
Using the task-based api makes your code clearer and portable since you are dealing with only logical dataflows amongst your problem-domain "tasks" and not any underlying OS/thread-library/machine level complexities.
No, the std::mutex is the reason you have a condition variable. Never mix std::atomic with std::mutex-and-friends, you are very likely introducing race conditions that way.