想知道为什么我不能只为协程捕获 asio::handler_type 的引用
Wondering why I couldn't just capture the reference of asio::handler_type for the coroutine
我正在使用协程玩 asio,想测试如何调用异步函数。我有以下代码:
void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
using handler_type = boost::asio::handler_type<decltype(yield), void()>::type;
handler_type handler(std::forward<handler_type>(yield));
boost::asio::async_result<decltype(handler)> result(handler);
auto timer(std::make_shared<boost::asio::deadline_timer>(io_service, boost::posix_time::seconds(1)));
// the program crashes if I use &handler here
timer->async_wait([&handler](const boost::system::error_code) {
std::cout << "enter" << std::endl;
handler();
std::cout << "done" << std::endl;
});
result.get();
std::cout << "function finished" << std::endl;
return;
}
int main()
{
boost::asio::io_service io_service;
boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield) {
std::cout << "hello" << std::endl;
async_foo(io_service, yield);
std::cout << "world" << std::endl;
});
io_service.run();
return 0;
}
奇怪的是,如果我将 &handler 放在捕获列表中,执行流程将被打乱,然后它会遇到分段错误。但是如果我改用 "handler" 它运行没有任何问题(当然我需要在 lambda 中复制)。
四处搜索,没有找到任何相关内容。在此先感谢您的帮助。
正如 Tanner 很好地解释的那样 here:
- While
spawn()
adds work to the io_service
(a handler that will start and jump to
the coroutine), the coroutine itself is not work. To prevent the
io_service
event loop from ending while a coroutine is outstanding,
it may be necessary to add work to the io_service
before yielding.
如果在 run()
之后添加额外的跟踪线:
io_service.run();
std::cout << "BYE" << std::endl;
然后,运行几次,直到你幸运的没有早早拿到SEGV,你可以看到如下输出:
hello
enter
done
BYE
的确,io_service returns,破坏挂起的 operations/handlers,这也破坏了 coro¹ 的堆栈上下文。展开该堆栈会破坏该堆栈上的(本地)变量:
- 处理程序
- 结果
- 计时器
并且由于 timer
未在 async_wait
的完成处理程序中捕获,计时器被简单地取消,但仍尝试调用现在引用现已失效的堆栈变量的完成处理程序handler
.
复制 handler
显然²确实让 coro 保持活力。
为了回答核心问题 "and would like to test how an async function can be called" 我建议使用更简单的成语:
void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(1));
boost::system::error_code ec;
timer.async_wait(yield[ec]);
¹ -fsanitize=address
证实了这一点
² 我知道它包含协程上下文的 weak_ptr
,所以也许 Asio 在内部将其锁定为 shared_ptr
,我自己不太确定这些实现细节
我正在使用协程玩 asio,想测试如何调用异步函数。我有以下代码:
void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
using handler_type = boost::asio::handler_type<decltype(yield), void()>::type;
handler_type handler(std::forward<handler_type>(yield));
boost::asio::async_result<decltype(handler)> result(handler);
auto timer(std::make_shared<boost::asio::deadline_timer>(io_service, boost::posix_time::seconds(1)));
// the program crashes if I use &handler here
timer->async_wait([&handler](const boost::system::error_code) {
std::cout << "enter" << std::endl;
handler();
std::cout << "done" << std::endl;
});
result.get();
std::cout << "function finished" << std::endl;
return;
}
int main()
{
boost::asio::io_service io_service;
boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield) {
std::cout << "hello" << std::endl;
async_foo(io_service, yield);
std::cout << "world" << std::endl;
});
io_service.run();
return 0;
}
奇怪的是,如果我将 &handler 放在捕获列表中,执行流程将被打乱,然后它会遇到分段错误。但是如果我改用 "handler" 它运行没有任何问题(当然我需要在 lambda 中复制)。
四处搜索,没有找到任何相关内容。在此先感谢您的帮助。
正如 Tanner 很好地解释的那样 here:
- While
spawn()
adds work to theio_service
(a handler that will start and jump to the coroutine), the coroutine itself is not work. To prevent theio_service
event loop from ending while a coroutine is outstanding, it may be necessary to add work to theio_service
before yielding.
如果在 run()
之后添加额外的跟踪线:
io_service.run();
std::cout << "BYE" << std::endl;
然后,运行几次,直到你幸运的没有早早拿到SEGV,你可以看到如下输出:
hello
enter
done
BYE
的确,io_service returns,破坏挂起的 operations/handlers,这也破坏了 coro¹ 的堆栈上下文。展开该堆栈会破坏该堆栈上的(本地)变量:
- 处理程序
- 结果
- 计时器
并且由于 timer
未在 async_wait
的完成处理程序中捕获,计时器被简单地取消,但仍尝试调用现在引用现已失效的堆栈变量的完成处理程序handler
.
复制 handler
显然²确实让 coro 保持活力。
为了回答核心问题 "and would like to test how an async function can be called" 我建议使用更简单的成语:
void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(1));
boost::system::error_code ec;
timer.async_wait(yield[ec]);
¹ -fsanitize=address
证实了这一点
² 我知道它包含协程上下文的 weak_ptr
,所以也许 Asio 在内部将其锁定为 shared_ptr
,我自己不太确定这些实现细节