3

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

#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

R.J
  • 396
  • 1
  • 13

1 Answers1

7

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"
sehe
  • 374,641
  • 47
  • 450
  • 633
  • "Oops" for sure. It makes sense now. Thanks – R.J Aug 24 '22 at 11:02
  • 1
    I'm going to stick with std::string by-value parameter. I want to avoid the potential situation when the function user, uses the function with temporaries, for example, using string_literals operators and etc. It seems, generally, it's a good practice to make sure values are being copied or moved into coroutines. What are your thoughts? – R.J Aug 24 '22 at 11:18
  • I'd agree. I just wanted to make sure you understood the options so you can choose – sehe Aug 24 '22 at 12:11