Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stopping boost::asio::io_service::run() from concurrent destructor

Can anybody explain me why this program does not terminate (see the comments)?

#include <boost/asio/io_service.hpp>
#include <boost/asio.hpp>
#include <memory>
#include <cstdio>
#include <iostream>
#include <future>

class Service {
public:
    ~Service() {
        std::cout << "Destroying...\n";
        io_service.post([this]() {
            std::cout << "clean and stop\n"; // does not get called
            // do some cleanup
            // ...
            io_service.stop();
            std::cout << "Bye!\n";
        });
        std::cout << "...destroyed\n"; // last printed line, blocks
    }

    void operator()() {
        io_service.run();
        std::cout << "run completed\n";
    }

private:
    boost::asio::io_service io_service;
    boost::asio::io_service::work work{io_service};
};

struct Test {
    void start() {
        f = std::async(std::launch::async, [this]() { service(); std::cout << "exiting thread\n";});
    }
    std::future<void> f;
    Service service;
};

int main(int argc, char* argv[]) {
    {
        Test test;
        test.start();

        std::string exit;
        std::cin >> exit;
    }

    std::cout << "exiting program\n"; // never printed
}
like image 214
Martin Avatar asked May 08 '16 23:05

Martin


People also ask

What is Boost :: ASIO :: Io_service?

Asio defines boost::asio::io_service , a single class for an I/O service object. Every program based on Boost. Asio uses an object of type boost::asio::io_service . This can also be a global variable. While there is only one class for an I/O service object, several classes for I/O objects exist.

How do I stop IO service boost?

Stopping the io_service from running out of work boost::asio::io_service io_service; boost::asio::io_service::work work(io_service); ... To effect a shutdown, the application will then need to call the io_service object's stop() member function.

Is boost :: ASIO :: Io_service thread safe?

Thread Safety In general, it is safe to make concurrent use of distinct objects, but unsafe to make concurrent use of a single object. However, types such as io_service provide a stronger guarantee that it is safe to use a single object concurrently.


1 Answers

The real issue is that destruction of io_service is (obviously) not thread-safe.

Just reset the work and join the thread. Optionally, set a flag so your IO operations know shutdown is in progress.

You Test and Service classes are trying to share responsibility for the IO service, that doesn't work. Here's much simplified, merging the classes and dropping the unused future.

Live On Coliru

The trick was to make the work object optional<>:

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

struct Service {
    ~Service() {
        std::cout << "clean and stop\n";
        io_service.post([this]() {
            work.reset(); // let io_service run out of work
        });

        if (worker.joinable())
            worker.join();
    }

    void start() {
        assert(!worker.joinable());
        worker = std::thread([this] { io_service.run(); std::cout << "exiting thread\n";});
    }

private:
    boost::asio::io_service io_service;
    std::thread worker;
    boost::optional<boost::asio::io_service::work> work{io_service};
};

int main() {
    {
        Service test;
        test.start();

        std::cin.ignore(1024, '\n');
        std::cout << "Start shutdown\n";
    }

    std::cout << "exiting program\n"; // never printed
}

Prints

Start shutdown
clean and stop
exiting thread
exiting program
like image 150
sehe Avatar answered Oct 13 '22 00:10

sehe