4
votes

From Asio documentation

The run() function blocks until all work has finished and there are no more handlers to be dispatched, or until the io_service has been stopped.

In the following snippet Asio has no work apart from debug output (which is instantly computed), but run() does not return.

#define BOOST_ASIO_ENABLE_HANDLER_TRACKING

#include <iostream>
#include <thread>

#include <boost/asio.hpp>

int main()
{
  namespace asio = boost::asio;

  asio::io_service ios;

  asio::ip::udp::endpoint ep(boost::asio::ip::udp::v4(), 9876);
  auto socket = new asio::ip::udp::socket(ios, ep);
  std::thread th([&]
  {
      ios.dispatch([]{ std::cout << "before run()" << std::endl;});
      ios.run();
      std::cout << "after run()" << std::endl;
  });

  std::this_thread::sleep_for(std::chrono::seconds(5)); // wait for io_service to launch

  socket->cancel();
  socket->close();
  delete socket; // just in case

  std::cout << "socket is closed" << std::endl;

  th.join(); // hangs here

  std::cout << "exiting..." << std::endl;
}

The output before hang is

@asio|1433598048.101578|0*1|[email protected]
@asio|1433598048.101785|>0|
before run()
socket is closed

Without socket this snippet works just fine.

I'm using Ubuntu 15.04 and I tried gcc-4.9.2, gcc-5.1, clang-3.6, boost-1.56 and boost-1.58.

Is this a bug and if so, is there any workaround, or I just misunderstand something?

UPDATE
This bug is reproducing only with following file, that must be compiled together with above snippet in other translation unit:

#include <boost/asio.hpp>

namespace asio = boost::asio;

class my_server
{
public:
  my_server(asio::io_service& ios);

private:

  asio::io_service& _ios;
  asio::ip::udp::socket _socket;
};

my_server::my_server(boost::asio::io_service &ios)
  : _ios(ios), _socket(ios, asio::ip::udp::endpoint())
{
}

I created minimal project at https://github.com/shadeware/asio-problem

1
Your verbatim code works for me on OS X (10.10.3) with clang 3.6 and Debian 8 with gcc 4.9. It does pause for a few seconds before the cancel() and close() are logged, but then completes as expected. - rhashimoto

1 Answers

2
votes

Apparently if you choose to define BOOST_ASIO_ENABLE_HANDLER_TRACKING then you must do so in all boost::asio translation units. I don't see this mentioned in the documentation, but I did find it on the Boost mailing list.

When I add

add_definitions(-DBOOST_ASIO_ENABLE_HANDLER_TRACKING)

to your CMakeLists.txt so it is applied globally then I don't see the hang.