chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.72k stars 1.19k forks source link

co_composed prematurely releases objects captured by the implementation lambda before the first co_await #1491

Open lnpg opened 2 weeks ago

lnpg commented 2 weeks ago
struct Func {
    int value = 1;

    Func() {
        std::cout << "Func() " << std::uintptr_t(this) << "\n";
    }

    Func(Func &&) {
        std::cout << "Func(Func&&)  " << std::uintptr_t(this) << "\n";
    }

    ~Func() {
        value = 0;
        std::cout << "~Func() " << std::uintptr_t(this) << "\n";
    }
};

template<asio::completion_token_for<void(asio::error_code)> Token>
auto asyncCoCompose(asio::io_context &ioCtx, Token &&token) {
    return asio::async_initiate<Token, void(asio::error_code)>(
        asio::experimental::co_composed<void(asio::error_code)>(
            [fun=Func{}](auto state, asio::io_context &ioCtx)-> void {
                asio::steady_timer timer{ioCtx};
                timer.expires_after(std::chrono::seconds{1});
                std::cout << "Before co_await " << std::uintptr_t(&fun) << " " << fun.value << "\n";
                co_await timer.async_wait(asio::deferred);
                std::cout << "After co_await " << std::uintptr_t(&fun) << " " << fun.value << "\n";
                co_return {};
            }
            , ioCtx)
        , token, std::ref(ioCtx));
}

int main() {
    asio::io_context ioCtx{};
    asyncCoCompose(ioCtx, [](asio::error_code ec) {
        std::cout << "finish\n";
    });

    ioCtx.run();

    return 0;
}

the lambda for implementation parameter of co_composed captures a Func object fun, which get destructed before co_await timer.async_wait(asio::deferred);