How to used boost::asio::async_result, why my code crash with (interrupted by signal 11: SIGSEGV)

using ReadSignature = void(int);
template <class CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken , ReadSignature)
AsyncRead(boost::asio::io_service* ios,CompletionToken&& token) {
  using Handler = typename boost::asio::handler_type<CompletionToken,
                                                 ReadSignature>::type;
  Handler handler(std::forward<CompletionToken>(token));
  boost::asio::async_result<Handler> result(handler);

  std::cout << std::time(nullptr) << ":before thread" << std::endl;

  std::thread thread([ios,&handler]() {
    std::cout << std::time(nullptr) << ":run in thread before sleep"<< std::endl;
    sleep(5);
    std::cout << std::time(nullptr) << ":run in thread after sleep"<< std::endl;
    std::cout << std::time(nullptr) << ":run in thread before cb" << std::endl;
    ios->post([&handler](){
      handler(2);
    });
    std::cout << std::time(nullptr) << ":run in thread after cb"<<std::endl;
  });
  thread.detach();
  return result.get();
}

int main(int argc, char** argv) {
  boost::asio::io_service s;
  boost::asio::io_service::work worker(s);
  boost::asio::spawn(s,[&s](boost::asio::yield_context yield){
    boost::system::error_code er;
    int val=AsyncRead(&s,yield[er]);
    std::cout << std::time(nullptr) <<"get:"<<val<< "" << std::endl;
  });

  s.run();
  return 0;
}

I expect : return result.get(); will yield fiber, and value 2 will get. but the code crash with:(interrupted by signal 11: SIGSEGV) ,because handler.ec is null.

1 Answers

1
rafix07 On Best Solutions

By this

ios->post([&handler](){
      handler(2);
    });

you create lambda which is queued in io_service. This closure is executed inside io_service::run. You capture by reference handler which is local inside AsyncRead. When handler(2) is called

AsyncRead(&s,yield[er]);

coroutine is resumed in above line, result.get() is called from AsyncRead, AsyncRead ends and handler as local variable is destroyed but closure which is executed in io_service::run still refers to this variable - undefined behaviour.

You need to capture handler by value, moving it into thread and lambda:

  std::thread thread([ios,handler = std::move(handler)]() {
    std::cout << std::time(nullptr) << ":run in thread before sleep"<< std::endl;
    sleep(5);
    std::cout << std::time(nullptr) << ":run in thread after sleep"<< std::endl;
    std::cout << std::time(nullptr) << ":run in thread before cb" << std::endl;
    ios->post([handler = std::move(handler)]() mutable {
      handler(2);
    });
    std::cout << std::time(nullptr) << ":run in thread after cb"<<std::endl;
  });