pandoragami
pandoragami

Reputation: 5585

BOOST::ASIO. Making sure io_service.run() is invoked only after async_receive(...) is called?

I have a problem where two threads are called like this, one after another.

new boost::thread( &SERVER::start_receive, this);

new boost::thread( &SERVER::run_io_service, this);

Where the first thread calls this function.

void start_receive()
{   
    udp_socket.async_receive(....);
}

and the second thread calls,

void run_io_service()
{  
    io_service.run();
}

and sometimes the io_service thread ends up finishing before the start_receive() thread and then the server cannot receive packets.

I thought about putting a sleep function between the two threads to wait a while for the start_receive() to complete and that works but I wondered if there was another sure fire way to make this happen?

Upvotes: 0

Views: 1977

Answers (2)

Richard Hodges
Richard Hodges

Reputation: 69892

When you call io_service.run(), the thread will block, dispatching posted handlers until either:

  1. There are no io_service::work objects associated with the io_service, or

  2. io_service.stop() is called.

If either of these happens, the io_service enters the stopped state and will refuse to dispatch any more handlers in future until its reset() method is called.

Every time you initiate an asynchronous operation on an io object associated with the io_service, an io_service::work object is embedded in the asynchronous handler.

For this reason, point (1) above cannot happen until the asynchronous handler has run.

this code therefore will guarantee that the async process completes and that the asserts pass:

asio::io_service ios;    // ios is not in stopped state
assert(!ios.stopped());
auto obj = some_io_object(ios);
bool completed = false;
obj.async_something(..., [&](auto const& ec) { completed = true; });
// nothing will happen yet. There is now 1 work object associated with ios
assert(!completed);

auto ran = ios.run();
assert(completed);
assert(ran == 1);    // only 1 async op waiting for completion.
assert(ios.stopped());  // io_service is exhausted and no work remaining

ios.reset();
assert(!ios.stopped());  // io_service is ready to run again 

Upvotes: 3

Chad
Chad

Reputation: 19032

If you want to keep the io_service running, create a work object:

boost::asio::io_service svc;
auto work = std::make_shared<boost::asio::io_service::work>(svc);
svc.run(); // this will block as long as the work object is valid.

The nice thing about this approach is that the work object above will keep the svc object "running", but not block any other operations on it.

Upvotes: 2

Related Questions