Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Boost asio C++ 20 Coroutines: co_spawn a coroutine with a by-reference parameter unexpected result

In the following code, the parameter of the session coroutine is passed by reference.

#include <boost/asio.hpp>
#include <iostream>

boost::asio::awaitable<void> session(const std::string& name)
{
    std::cout << "Starting " << name << std::endl;
    auto executor = co_await boost::asio::this_coro::executor;
}

int main()
{
    boost::asio::io_context io_context;

    co_spawn(io_context, session("ServerA"), boost::asio::detached);
    co_spawn(io_context, session("ServerB"), boost::asio::detached);

    io_context.run();

    return 0;
}

For some reason that I don’t understand, the above code results in printing Starting ServerB twice.

> g++ -std=c++20 ../test-coro.cpp -o test-coro && ./test-coro
Starting ServerB
Starting ServerB

But when I change the coroutine parameter to pass by value, it will correctly print both Starting ServerA and Starting ServerB

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#include <boost/asio.hpp>
#include <iostream>

boost::asio::awaitable<void> session(std::string name)
{
    std::cout << "Starting " << name << std::endl;
    auto executor = co_await boost::asio::this_coro::executor;
}

int main()
{
    boost::asio::io_context io_context;

    co_spawn(io_context, session("ServerA"), boost::asio::detached);
    co_spawn(io_context, session("ServerB"), boost::asio::detached);

    io_context.run();

    return 0;
}
> g++ -std=c++20 ../test-coro.cpp -o test-coro && ./test-coro
Starting ServerA
Starting ServerB

Is it expected or this is a compiler/library bug? If it is expected, then what’s the reasoning for it?

Environment:
Arch Linux 5.18.16-arch1-1
gcc (GCC) 12.2.0
boost version 1.79

>Solution :

You can think of the coroutine state as containing what would be on the function call stack (which is what makes the function resumable): cppreference

When a coroutine begins execution, it performs the following:

  • allocates the coroutine state object using operator new (see below)
  • copies all function parameters to the coroutine state: by-value parameters are moved or copied, by-reference parameters remain
    references (and so may become dangling if the coroutine is resumed
    after the lifetime of referred object ends)

The coroutine logically stores a reference to a temporary string. Oops.

I haven’t checked, but I’d assume that Asio’s awaitable implementation starts with an initial suspend_always (this makes intuitive sense to me with respect to the executor model).

Yes, this means that co_spawn with any reference argument means the lifetime of the object referenced must be guaranteed.

On my system, the output was merely

Starting
Starting

One fix is what you showed. To illustrate out the lifetime aspect:

{
    std::string a="ServerA", b="ServerB";
    co_spawn(io_context, session(a), boost::asio::detached);
    co_spawn(io_context, session(b), boost::asio::detached);

    io_context.run();
}

Is also a valid fix. For the trivial example I’d suggest a std::string_view regardless:

Live On Coliru

#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>

boost::asio::awaitable<void> session(std::string_view name)
{
    std::cout << "Starting " << std::quoted(name) << std::endl;
    auto executor = co_await boost::asio::this_coro::executor;
}

int main()
{
    boost::asio::io_context io_context;

    std::string const a="ServerA";
    co_spawn(io_context, session(a), boost::asio::detached);
    co_spawn(io_context, session("ServerB"), boost::asio::detached);

    io_context.run();
}

Printing

Starting "ServerA"
Starting "ServerB"
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading