Boost.Asio async_compose 在负载下卡住

Ale*_*mer 5 c++ boost boost-asio boost-coroutine

我继承了一个正在boost 1.75上工作的代码库。此代码在 boost 1.75 上运行没有问题:

using CompletionTokenType = boost::asio::yield_context;
using FunctionType = void(boost::system::error_code);
using AsyncResultType = boost::asio::async_result<CompletionTokenType, FunctionType>;
using HandlerType = typename AsyncResultType::completion_handler_type;

[[maybe_unused]] ResultOrErrorType
read(CompletionTokenType token, StatementType const& statement)
{
    auto handler = HandlerType{token};
    auto result = AsyncResultType{handler};
    
    auto const future = handle_.get().asyncExecute(statement, [handler](auto const&) mutable {
        boost::asio::post(boost::asio::get_associated_executor(handler), [handler]() mutable {
            handler(boost::system::error_code{});
        });
    });

    result.get(); // suspends coroutine until handler called

    if (auto res = future.get(); res) {
        return res;
    } else {
        // handle error
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们被迫迁移到boost 1.82async_compose ,随之而来的是使用/的必要性async_initiate(据我所知)。

代码必须更改为以下混乱。

  • 我们现在必须做出 a shared_ptrout,self因为给定的回调是复制回调的asyncExecutea 。std::function可悲的是,与以前self不同,它是不可复制的。handler
  • “回调地狱”而不是通常与协程相关的稍微更顺序的代码
[[maybe_unused]] ResultOrErrorType
read(CompletionTokenType token, StatementType const& statement)
{
    auto future = std::optional<FutureWithCallbackType>{};
    
    auto init = [this, &statement, &future]<typename Self>(Self& self) {
        future.emplace(handle_.get().asyncExecute(statement, [sself = std::make_shared<Self>(std::move(self))](auto&& data) mutable {
                auto executor = asio::get_associated_executor(*sself);
                asio::post(executor, [data = std::move(data), sself = std::move(sself)]() mutable {
                    sself->complete(std::move(data));
                });
        }));
    };

    auto res = asio::async_compose<CompletionTokenType, void(ResultOrErrorType)>(init, token, boost::asio::get_associated_executor(token));

    if (res) {
        return res;
    } else {
        // handle error
    }
}
Run Code Online (Sandbox Code Playgroud)

这个新代码在大多数情况下都可以正常工作,但最终在输入后会卡住async_compose。我没有看到路径中的任何日志handle error,并且通常假定数据库代码可以正常工作(来自多个其他测试)。调用中始终会触发一个回调asyncExecute

上面的代码稍微简化了。例如,在 MacOS 上,上面的代码按原样工作,但在 Linux(gcc) 上,我们需要向内部添加一个显式工作对象post,否则应用程序将崩溃。另外,在 MacOS 上,可以sself->complete在不使用 a 包装的情况下进行调用post,但在 Linux 上,这又会导致应用程序崩溃。

所有这些都表明这段代码有些不太正确。请帮助我了解我们做错了什么。任何想法/指示将不胜感激。

如果有办法让旧代码再次运行,这也是一个可行的解决方案。然而,我无法做到这一点,因为completion_handler_type即使 boost 1.82 的文档声称仍然公开它,它似乎已经消失了。