bobeff bobeff - 1 month ago 20
C++ Question

How to wait for all pending completion handlers to be executed before stopping io_service?

I'm writing a server using boost::asio. I have multiple threads each owning it's own

io_service
object. I'm using
io_service::work
object to keep
io_service
running when there is no completion handlers to execute. In certain moment I'm calling
stop
method of
io_service
objects to finish threads which called
io_serice::run
. But in some cases when I'm calling
stop
I have posted in
io_service
object competion handlers which are not finished. Calling
stop
prevents posted competion handlers from executing, but this is unacceptable for me because I need all pending work to be finished before stopping the thread. How to wait for all pending completion handlers first to be executed before calling
stop
method of
io_service
?

Answer

Just reset the work.

Any io_service::run() will return when all pending work has been completed.

The common pattern is to use optional.work() so you can clear it. If it is a frequent thing, you could reduce some mental overhead by wrapping it in a RAII-enabled class:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/optional.hpp>

struct work {
    using io_service = boost::asio::io_service;
    work(io_service& svc) : _work(io_service::work(svc)) 
    { }

    void release() {
        _work.reset();
    }

    void enlist(io_service& svc) {
        _work.emplace(io_service::work(svc));
    }

  private:
    boost::optional<io_service::work> _work;
};

#include <thread>
#include <iostream>
using namespace std::chrono_literals;

int main() {
    boost::asio::io_service svc;
    work lock(svc);

    std::thread background([&] { svc.run(); });

    std::this_thread::sleep_for(1s);

    std::cout << "releasing work";
    lock.release();

    background.join();
}

Which finishes the background thread after 1 second.