jeudi 14 février 2019

How to exit ASIO timer resources in order to exit thread properly?

I have the following MyThread class that is run as a thread. This class starts a heartbeat timer (boost deadline_timer) after 30 seconds and runs every 30 seconds unless canceled. If I comment all the lines that have anything to do with boost ASIO out, my thread exits and joins properly.

However, with the ASIO code uncommented, and once I tell my thread to stop (using future and promise in Stoppable base), then my program will crash with the following:

terminate called without an active exception

Which as I understand means that I haven't joined or detached the threads within MyThread, specifically ASIO. I feel like the deadline_timer thread hasn't exited as it should by calling cancel, and even if I call io.stop, same results.

class MyThread : Stoppable {
    private:
        boost::asio::io_service io;

        std::shared_ptr<boost::asio::deadline_timer> timer;
        // ...
}

void MyThread::operator()() {
    timer = std::make_shared<boost::asio::deadline_timer>(io,
        boost::posix_time::seconds(30));

    timer->async_wait(boost::bind(&MyThread::beat, this,
        boost::asio::placeholders::error));

    std::unique_ptr<std::thread> io_thread(
        std::make_unique<std::thread>([&] { io.run(); }));

    while (stop_requested() == false) {
        // ...
        // before stop() on the thread is called in main process,
        // close_resources() is called.
    }
}

void MyThread::beat(const boost::system::error_code &ec) {
    if (ec == boost::asio::error::operation_aborted) {
        // already cancelled
        return;
    }

    timer->expires_at(hb_timer->expires_at() + boost::posix_time::seconds(30);
    timer->async_wait(boost::bind(&MasterMode::beat, this, 
        boost::asio::placeholders::error)); 

    send_beat();
}

void MyThread::close_resources() {
    timer->get_io_service().post([&] { 
        int cancelled = timer->cancel(); 

        std::cout << "    # cancelled = " << cancelled << std::endl;
    });

    io.stop();
}

In my main process, I start MyThread class, then stop it and join as follows. This works when I don't include the ASIO code above.

{ 
    my_class = std::make_shared<MyThread>();

    my_thread = std::make_shared<std::thread>([&] { (*my_class)(); });

    // ...

    my_class->stop();

    my_thread->join();
}

Aucun commentaire:

Enregistrer un commentaire