boost :: asio :: yield_context:意外的forced_unwind异常

 甜甜deX 发布于 2022-12-10 18:16

我特林写我的自定义异步功能的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_contextCompletionToken维持weak_ptr到协程.

handler_type::type构造专用处理程序时,它shared_ptr通过CompletionToken 获取协同程序weak_ptr.当处理程序作为完成处理程序传递给异步操作时,将shared_ptr复制处理程序及其处理程序.调用处理程序时,它将恢复协程.

在调用时async_result::get(),特化将重置shared_ptrasync_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钩子.

1 个回答
  • 简而言之,您需要创建一个处理程序的副本,例如通过将其发布到io_service,然后尝试获取async_result以保持协程存活.


    Boost.Asio通过破坏协程来防止不可恢复的协程无限期地暂停,导致协程的堆栈放松.协程对象将boost::coroutines::detail::forced_unwind在其销毁期间抛出,导致挂起的堆栈放松.Asio通过以下方式实现此目的:

    yield_contextCompletionToken维持weak_ptr到协程.

    handler_type::type构造专用处理程序时,它shared_ptr通过CompletionToken 获取协同程序weak_ptr.当处理程序作为完成处理程序传递给异步操作时,将shared_ptr复制处理程序及其处理程序.调用处理程序时,它将恢复协程.

    在调用时async_result::get(),特化将重置shared_ptrasync_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钩子.

    2022-12-11 02:09 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有