2017-04-14 27 views
0

我正在使用协程来玩asio,并想测试如何调用异步函数。我有以下代码:想知道为什么我不能只捕获协程的asio :: handler_type的引用

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; 
} 

这很奇怪,如果我把&处理程序捕获列表中的执行流将被搞砸了,然后它击中分段错误。但是,如果我使用“处理程序”而不是任何问题运行(然后我需要在lambda当然副本)。

搜索过,找不到任何相关的内容。预先感谢您的帮助。

回答

0

正如坦纳非常漂亮解释here

  • 虽然spawn() 增加了工作的io_service(处理程序将启动,并跳转到 协程),协程本身是不行的。为了防止在协程未完成时结束事件循环, 可能需要在产生之前将工作添加到io_service

如果您在run()后添加一个额外的迹线:

io_service.run(); 
std::cout << "BYE" << std::endl; 

然后,运行几次,直到你足够幸运,不及早获得SEGV,你可以看到输出像以下内容:

hello 
enter 
done 
BYE 

实际上,io_service对象的回报,破坏待处理操作/处理程序,这也破坏了coro¹的堆叠上下文。展开该堆栈自毁住该堆栈上的(局部)变量:

  • 处理
  • 结果
  • 定时器

而且,由于timer没有在完成处理程序捕获async_wait,该定时器简单地取消,但仍尝试调用完成处理程序,该处理程序现在引用一个现已解散的堆栈变量handler

复制handler显然²确保科罗活着。


要回答的核心问题“想测试一个异步功能如何可以被称为”我建议更简单的成语:

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]); 

看到它Live On Coliru


¹-fsanitize=address证实了这一点 ²我知道它包含了weak_ptr到协程范围内,所以也许短耳内部锁定了成shared_ptr,不太清楚这些实施细则自己

+0

谢谢你很多关于详细的说明。我在ctor和dtor中放置了一个输出的测试对象,显然在定时器触发之前有一个堆栈展开。 –

相关问题