Search code examples
c++boostc++20boost-asioc++-coroutine

What is the difference between boost::asio::use_awaitable and boost::asio::deferred when used with C++20 coroutines?


When using C++20 coroutines with Boost.Asio it is possible to make an operation awaitable with either boost::asio::use_awaitable or boost::asio::deferred as in the following examples.

std::size_t n = co_await my_socket.async_read_some(buffer, use_awaitable);
std::size_t n = co_await my_socket.async_read_some(buffer, deferred);

What is the difference and in what situations would you use one rather than the other?

Specifically, I have seen several places on the internet where deferred is described as higher performance because it "doesn't create a coroutine frame".

[asio::deferred] provides a significant improvement in performance for operations that don’t need the full functionality of the asio::awaitable<> type. https://cppalliance.org/richard/2022/08/10/RichardsAugustUpdate.html

By using asio::deferred you can avoid creation of a new coroutine frame. https://www.reddit.com/r/cpp/comments/10idtq0/an_implementation_of_smpp_protocol_on_boostasio/

The overhead is minimal, specially if you know how to avoid creating unnecessary coroutine frames e.g. use_awaitable vs deferred. https://www.reddit.com/r/cpp_questions/comments/14kq0ew/should_almost_all_new_asio_code_use_coroutines/


Solution

  • Boost.Asio coroutines work by allocating an awaitable_frame to store the local variables and then dispatching the execution through the executor.

    The use_awaitable completion token creates a coroutine frame wrapping the async initiation function and returns it.

    The deferred completion token returns a deferred function object. A deferred function is not a coroutine and can be run without using coroutines at all. The asio coroutine promise knows how to dispatch the deferred function to the executor directly without needing to allocate an additional coroutine frame.

    Therefore, deferred really does avoid the creation of an additional coroutine frame.

    deferred should be preferred in general, however the type it returns is obtuse and therefore it can not easily be made part of an API or stored in containers, etc.

    The advantage of use_awaitable is that it always returns an awaitable<>.