I was working with gcc 10 and coroutines on wsl 1 on windows 10 and noticed a that the arguments passed to the function that returns the awaiter is destroyed by the time the coroutine is suspended

clang and msvc doesn't do this but keep the temporaries until the expression is completed

code demonstrating the problem:

#include <cstdio>
#include <thread>
#if defined __clang__ || defined _WIN32
#include <experimental/coroutine>
using coro_handle = std::experimental::coroutine_handle<>;
using suspend_never = std::experimental::suspend_never;
#else
#include <coroutine>
using coro_handle = std::coroutine_handle<>;
using suspend_never = std::suspend_never;
#endif // __clang__


struct arg_t
{
    arg_t() { printf("arg_t()\n"); }

    ~arg_t()
    {
        printf("~arg_t()\n");
    }
};

struct awaiter_t
{
    awaiter_t() { printf("awaiter_t()\n"); }

    ~awaiter_t()
    {
        printf("~awaiter_t()\n");
    }

    bool await_ready() const noexcept 
    {
        printf("await_ready()\n");
        return false;
    }

    void await_suspend(coro_handle coro)
    {
        printf("await_suspend()\n");
        std::thread([coro]() mutable { std::this_thread::sleep_for(std::chrono::seconds(3)); coro.resume(); }).detach();
    }

    void await_resume()
    {
        printf("await_resume()\n");
    }
};

struct task_t
{
    struct promise_type
    {

        task_t get_return_object() { return {}; }

        suspend_never initial_suspend() noexcept { return {}; }

        suspend_never final_suspend() noexcept { return {}; }

        void unhandled_exception() noexcept { std::terminate(); }

        void return_void() {}

    };

    task_t() = default;

};

awaiter_t make_awaiter(const arg_t& arg)
{
    return awaiter_t{};
}


task_t test_awaiter()
{
    co_await make_awaiter(arg_t{});
}

int main()
{
    test_awaiter();
    std::this_thread::sleep_for(std::chrono::seconds(4));
    return 0;
}

gcc test: https://godbolt.org/z/cT4q8d

compiled with gcc flags : -std=c++20 -Wall -pthread -fcoroutines

output:

arg_t()
awaiter_t()
~arg_t() -> destroyed before await_ready !
await_ready()
await_suspend()
await_resume()
~awaiter_t()

clang test: https://godbolt.org/z/fcrzPM

compiled with clang flags : -std=c++20 -Wall -pthread -stdlib=libc++

output:

arg_t()
awaiter_t()
await_ready()
await_suspend()
await_resume()
~awaiter_t()
~arg_t() -> destroyed after the awaiter is destroyed

msvc also behaves like clang as I experimented here : Do temporaries passed to a function that returns awaitable remains valid after suspension point with co_await

is it a bug in gcc ? or the compiler is free to do whatever it likes here ?

0

There are 0 best solutions below