我特林写我的自定义异步功能的boost :: ASIO描述这里.
不过我与行获得的boost ::协程::详细:: forced_unwind例外result.get
#include#include #include #include #include namespace asio = ::boost::asio; template auto my_timer (Timer& timer, Token&& token) { typename asio::handler_type ::type handler (std::forward (token)); asio::async_result result (handler); timer.async_wait (handler); return result.get (); // Got forced_unwind exception here. } int main () { asio::io_service io; asio::steady_timer timer (io, ::boost::chrono::seconds (1)); asio::spawn (io, [&] (asio::yield_context yield) { try { std::cout << "my_timer enter\n"; my_timer (timer, yield); std::cout << "my_timer returns\n"; } catch (const boost::coroutines::detail::forced_unwind& e) { std::cout << "boost::coroutines::detail::forced_unwind\n"; } } ); io.run (); }
Coliru上的代码相同
更新:
行为存在于:
Darwin 14.0.0 (MacOS 10.10) clang version 3.6.0 (trunk 216817) and gcc version 4.9.1 (MacPorts gcc49 4.9.1_1) boost 1.57
和
Red Hat 6.5 gcc version 4.7.2 20121015 (Red Hat 4.7.2-5) (GCC) boost 1.57 and 1.56 (the example code was trivially modified because gcc 4.7 does not support c++14 mode)
Tanner Sansb.. 9
简而言之,您需要创建一个处理程序的副本,例如通过将其发布到io_service
,然后尝试获取async_result
以保持协程存活.
Boost.Asio通过破坏协程来防止不可恢复的协程无限期地暂停,导致协程的堆栈放松.协程对象将boost::coroutines::detail::forced_unwind
在其销毁期间抛出,导致挂起的堆栈放松.Asio通过以下方式实现此目的:
该yield_context
CompletionToken维持weak_ptr
到协程.
handler_type::type
构造专用处理程序时,它shared_ptr
通过CompletionToken 获取协同程序weak_ptr
.当处理程序作为完成处理程序传递给异步操作时,将shared_ptr
复制处理程序及其处理程序.调用处理程序时,它将恢复协程.
在调用时async_result::get()
,特化将重置shared_ptr
由async_result
构造期间传递给的处理程序所拥有的协程,然后生成协程.
这是尝试说明代码的执行.路径|
指示活动堆栈,:
指示挂起的堆栈,箭头用于指示控制转移:
boost::asio::io_service io_service;
boost::asio::spawn(io_service, &my_timer);
`-- dispatch a coroutine creator
into the io_service.
io_service.run();
|-- invoke the coroutine entry
| handler.
| |-- create coroutine
| | (count: 1)
| |-- start coroutine ----> my_timer()
: : |-- create handler1 (count: 2)
: : |-- create asnyc_result1(handler1)
: : |-- timer.async_wait(handler)
: : | |-- create handler2 (count: 3)
: : | |-- create async_result2(handler2)
: : | |-- create operation and copy
: : | | handler3 (count: 4)
: : | `-- async_result2.get()
: : | |-- handler2.reset() (count: 3)
| `-- return <---- | `-- yield
| `-- ~entry handler :
| (count: 2) :
|-- io_service has work (the :
| async_wait operation) :
| ...async wait completes... :
|-- invoke handler3 :
| |-- resume ----> |-- async_result1.get()
: : | |-- handler1.reset() (count: 1)
| `-- return <---- | `-- yield
| `-- ~handler3 : :
| | (count: 0) : :
| `-- ~coroutine() ----> | `-- throw forced_unwind
要解决此问题,handler
需要asio_handler_invoke()
在恢复协程时复制和调用.例如,以下内容将完成处理程序1发布到io_service
调用以下内容的副本handler
:
timer.async_wait (handler);
timer.get_io_service().post(
std::bind([](decltype(handler) handler)
{
boost::system::error_code error;
// Handler must be invoked through asio_handler_invoke hooks
// to properly synchronize with the coroutine's execution
// context.
using boost::asio::asio_handler_invoke;
asio_handler_invoke(std::bind(handler, error), &handler);
}, handler)
);
return result.get ();
作为展示在这里,这个额外的代码,输出变为:
my_timer enter
my_timer returns
1.完成处理程序代码可能会被清理一下,但是当我回答如何从不同的线程恢复Boost.Asio堆栈协程时,我观察到一些编译器选择了错误的asio_handler_invoke
钩子.
简而言之,您需要创建一个处理程序的副本,例如通过将其发布到io_service
,然后尝试获取async_result
以保持协程存活.
Boost.Asio通过破坏协程来防止不可恢复的协程无限期地暂停,导致协程的堆栈放松.协程对象将boost::coroutines::detail::forced_unwind
在其销毁期间抛出,导致挂起的堆栈放松.Asio通过以下方式实现此目的:
该yield_context
CompletionToken维持weak_ptr
到协程.
handler_type::type
构造专用处理程序时,它shared_ptr
通过CompletionToken 获取协同程序weak_ptr
.当处理程序作为完成处理程序传递给异步操作时,将shared_ptr
复制处理程序及其处理程序.调用处理程序时,它将恢复协程.
在调用时async_result::get()
,特化将重置shared_ptr
由async_result
构造期间传递给的处理程序所拥有的协程,然后生成协程.
这是尝试说明代码的执行.路径|
指示活动堆栈,:
指示挂起的堆栈,箭头用于指示控制转移:
boost::asio::io_service io_service;
boost::asio::spawn(io_service, &my_timer);
`-- dispatch a coroutine creator
into the io_service.
io_service.run();
|-- invoke the coroutine entry
| handler.
| |-- create coroutine
| | (count: 1)
| |-- start coroutine ----> my_timer()
: : |-- create handler1 (count: 2)
: : |-- create asnyc_result1(handler1)
: : |-- timer.async_wait(handler)
: : | |-- create handler2 (count: 3)
: : | |-- create async_result2(handler2)
: : | |-- create operation and copy
: : | | handler3 (count: 4)
: : | `-- async_result2.get()
: : | |-- handler2.reset() (count: 3)
| `-- return <---- | `-- yield
| `-- ~entry handler :
| (count: 2) :
|-- io_service has work (the :
| async_wait operation) :
| ...async wait completes... :
|-- invoke handler3 :
| |-- resume ----> |-- async_result1.get()
: : | |-- handler1.reset() (count: 1)
| `-- return <---- | `-- yield
| `-- ~handler3 : :
| | (count: 0) : :
| `-- ~coroutine() ----> | `-- throw forced_unwind
要解决此问题,handler
需要asio_handler_invoke()
在恢复协程时复制和调用.例如,以下内容将完成处理程序1发布到io_service
调用以下内容的副本handler
:
timer.async_wait (handler);
timer.get_io_service().post(
std::bind([](decltype(handler) handler)
{
boost::system::error_code error;
// Handler must be invoked through asio_handler_invoke hooks
// to properly synchronize with the coroutine's execution
// context.
using boost::asio::asio_handler_invoke;
asio_handler_invoke(std::bind(handler, error), &handler);
}, handler)
);
return result.get ();
作为展示在这里,这个额外的代码,输出变为:
my_timer enter
my_timer returns
1.完成处理程序代码可能会被清理一下,但是当我回答如何从不同的线程恢复Boost.Asio堆栈协程时,我观察到一些编译器选择了错误的asio_handler_invoke
钩子.