You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The lazy_task implementation currently unconditionally suspends the awaiter before starting execution of the task and then unconditionally resumes the awaiter when it reaches the final_suspend point.
This approach means that we don't need any synchronisation (ie. std::atomic usages) to coordinate awaiter and task.
However, it has the downside that if the lazy_task completes synchronously then the awaiter is recursively resumed. This can potentially consume a little bit of stack space every time a coroutine awaits the a lazy_task that completes synchronously if the compiler is not able to perform tail-call optimisation on the calls to void await_suspend() and void coroutine_handle<>::resume() (note that MSVC is not currently able to perform this tail-call optimisation and Clang only does this under optimised builds).
If the coroutine is awaiting lazy_task values in a loop and can possibly have a large number of these tasks complete synchronously then this could lead to stack-overflow.
eg.
lazy_task<int> async_one() { co_return1; }
lazy_task<int> sum()
{
int result = 0;
for (int i = 0; i < 1'000'000; ++i)
{
result += co_awaitasync_one();
}
co_return result;
}
The lazy_task implementation needs to be modified to not recursively resume the awaiter in the case that it completes synchronously. This, unfortunately means it's going to need std::atomic to decide the race between the awaiter suspending and the task completing.
The text was updated successfully, but these errors were encountered:
Note that @GorNishanov's https://github.com/GorNishanov/clang/tree/tailcall branch contains an experimental implementation of Clang that supports a new kind of await_suspend() overload that returns a coroutine_handle<> which can potentially allow guaranteed tail-call recursion.
Commit 316ce6c has made the change to use a synchronous-completion-safe implementation that uses atomics.
I'll reinstate a more efficient implementation once we have coroutine_handle-returning await_suspend() available in both MSVC and Clang (assuming it passes muster at the C++ standards meetings ;)
The
lazy_task
implementation currently unconditionally suspends the awaiter before starting execution of the task and then unconditionally resumes the awaiter when it reaches thefinal_suspend
point.This approach means that we don't need any synchronisation (ie.
std::atomic
usages) to coordinate awaiter and task.However, it has the downside that if the
lazy_task
completes synchronously then the awaiter is recursively resumed. This can potentially consume a little bit of stack space every time a coroutine awaits the alazy_task
that completes synchronously if the compiler is not able to perform tail-call optimisation on the calls tovoid await_suspend()
andvoid coroutine_handle<>::resume()
(note that MSVC is not currently able to perform this tail-call optimisation and Clang only does this under optimised builds).If the coroutine is awaiting
lazy_task
values in a loop and can possibly have a large number of these tasks complete synchronously then this could lead to stack-overflow.eg.
The
lazy_task
implementation needs to be modified to not recursively resume the awaiter in the case that it completes synchronously. This, unfortunately means it's going to needstd::atomic
to decide the race between the awaiter suspending and the task completing.The text was updated successfully, but these errors were encountered: