概述
当我使用BoostAsio的CompletionToken工具时,我设计从最终调用的CompletionToken
生成completion_handler
。
但是我对completion_handler
既没有被调用也没有被存储的行为感兴趣,在这种情况下,completion_handler
只是被销毁了。
我已经使用use_awaitable
CompletionToken测试了C++20协程的行为。然后我观察到有趣的行为。在completion_handler
销毁路径执行co_await
之后,堆栈被展开,ioc.run()
在int main()
中完成。在这个过程中,没有捕获异常。这是预期的行为还是未定义的行为?如果是预期的行为,文档在哪里?我还没找到
代码
#include <iostream>
#include <boost/asio.hpp>
namespace as = boost::asio;
template <typename CompletionToken>
auto func(bool call, CompletionToken&& token) {
auto init =
[]
(
auto completion_handler,
bool call
) {
if (call) {
std::move(completion_handler)();
}
else {
// What is happend if completion_handler is neither invoked nor stored ?
}
};
return as::async_initiate<
CompletionToken,
void()
>(
init,
token,
call
);
}
struct trace {
trace() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
~trace() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
as::awaitable<void> proc1() {
trace t; // destructed correctly
try {
{
std::cout << "before call=true" << std::endl;
co_await func(true, as::use_awaitable);
std::cout << "after call=true" << std::endl;
}
{
std::cout << "before call=false" << std::endl;
co_await func(false, as::use_awaitable);
// the following part is never executed
std::cout << "after call=false" << std::endl;
}
}
catch (...) {
std::cout << "caught exception" << std::endl;
}
std::cout << "co_return" << std::endl;
co_return;
}
as::awaitable<void> proc2() {
for (int i = 0; i != 2; ++i) {
std::cout << "before proc1" << std::endl;
co_await proc1();
std::cout << "after proc1" << std::endl;
}
}
int main() {
as::io_context ioc;
as::co_spawn(ioc.get_executor(), proc2, as::detached);
ioc.run();
std::cout << "finish" << std::endl;
}
字符串
输出
before proc1
trace::trace()
before call=true
after call=true
before call=false
trace::~trace()
finish
型
godbolt链接:https://godbolt.org/z/3dzoYban6
注意
在实践中,我不使用“might uninvokable”token,我使用基于错误代码的方法。
template <typename CompletionToken>
auto func(bool call, CompletionToken&& token) {
auto init =
[]
(
auto completion_handler,
bool call
) {
if (call) {
std::move(completion_handler)(boost::system::error_code{});
}
else {
// invoke with error code
std::move(completion_handler)(
boost::system::errc::make_error_code(
boost::system::errc::operation_canceled
)
);
}
};
return as::async_initiate<
CompletionToken,
void(boost::syste::error_code const&)
>(
init,
token,
call
);
}
型
1条答案
按热度按时间u4dcyp6a1#
completion_handler类型为
字符串
awaitable_handler
间接继承自awaitable_thread
,它在销毁时确实负责堆栈展开:型
通常,所有权从一个处理程序转移到另一个处理程序,但是在这里,没有任何东西再引用它,所以它不再存在。
虽然这是实现细节,但它使得如果科罗永远无法恢复,引用计数将变为零。此外,沿着有一些不错的代码注解,您可能会发现有见地,例如,
impl/awaitable.hpp
以型
你呢?
我找到了一个更老的答案,坦纳·桑斯伯里解释了堆栈式协同程序的等效语义:
协程将被挂起,直到操作完成并调用完成处理程序,io_service被销毁,或Boost.Asio检测到协程已被挂起且无法恢复,此时Boost.Asio将销毁协程。