marton78
marton78

Reputation: 3989

Canonical way to stop boost::asio operations

I have a boost::asio::io_service running in a thread that performs some operation:

struct clicker
{
    clicker(boost::asio::io_service& io) : timer_(io) { wait_for_timer(); }

    void stop() { timer_.cancel(); }

    void wait_for_timer()
    {
        timer_.expires_from_now(std::chrono::milliseconds(500));
        timer_.async_wait(std::bind(&clicker::wait_completed, this, _1));
    }

    void wait_completed(const boost::system::error_code& err)
    {
        if (!err) {
            std::cout << "Click" << std::endl;
            wait_for_timer();
        }
    }

    boost::asio::steady_timer timer_;
};

int main()
{
    boost::asio::io_service io;
    clicker cl(io);

    std::thread io_thread(&boost::asio::io_service::run, &io);

    while (true) { // the run loop
        // gather input
        if (user_clicked_stop_button()) { cl.stop(); break; }
    }

    io_thread.join();
}

Now calling stop() should cancel waiting for the timer and fire wait_completed() with an error. However, we have a race condition here: at times, stop() will be called while wait_for_timer() is running and before the async_wait has been scheduled. Then, the code will run indefinitely.

What's the recommended way to deal with this situation? A boolean flag inside clicker that is tested in wait_completed? A mutex?

Update:

This is just a simplified example, in the real code I have several operations running in the io_service, so calling io_service::stop() is not an option here.

Upvotes: 0

Views: 2629

Answers (2)

Igor R.
Igor R.

Reputation: 15075

Post the action to the io_service thread:

void stop() 
{ 
  timer_.get_io_service().post([=] { timer_.cancel(); }); 
} 

(If your compiler is not c++11-compatible, use bind to create the lambda.)

Upvotes: 2

Dan
Dan

Reputation: 1389

I think you want to call io.stop() to stop the io_service object.

Upvotes: 0

Related Questions