According to my knowledge and as far as I checked the boost::asio documentation and source code there is no way to destroy explicitly all pending handlers on a given io_context aside from destroying the context itself?
I need to be able, if possible, to stop the io_context, destroy the pending handlers on the io_context, then do some other things and finally destroy all io objects (timers, pipes, etc) associated with the given io_context and the io_context itself.
I know that I can use work_guard::reset and let all pending handlers to run and then the io_context will stop by itself, but the problem is that many of the handlers may produce (post/defer/etc) new pending handlers, etc i.e. each such handler will need to be guarded with something like 'if stopped'.
I think that the io_context::shutdown does exactly this but there is no way, aside from inheritance maybe, to call explicitly the shutdown function because it's not public.
Thanks.
The io_context class provides the core I/O functionality for users of the asynchronous I/O objects, including: boost::asio::ip::tcp::socket. boost::asio::ip::tcp::acceptor. boost::asio::ip::udp::socket. deadline_timer .
Asio (besides cross-platform work) is, that on each platform, it uses most effective strategy ( epoll on Linux 2.6, kqueue on FreeBSD/MacOSX, Overlapped IO on MS Windows).
Boost. Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach. Overview. An overview of the features included in Boost. Asio, plus rationale and design information.
boost::asio::post takes any callable object. Requirements for such object you can find here. There are many ways to achive what you want: [1] lambda expressions boost::asio::post(tp, [i]{ printProduct(i); });
Trying your suggestion using the protected shutdown results in a segfault on my system. I think it's protected for a reason :)
Anyhow, it looks like a judicious combination of restart/stop/reset might do the job. It's weird that some of the handler queue apparently stays around UNLESS one does a (empty) run/run_one. In fact even a poll_one seems to suffice. So, by all means, include that.
Here's my test bed code, you might find it useful:
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;
struct Handler {
    void operator()(boost::system::error_code ec) { std::cout << "Handler invoked: " << ec.message() << std::endl; }
    struct Instance { // logging only unique instance to avoid noise of moved handlers
        Instance()  { std::cout << "Created handler instance"   << std::endl; }
        ~Instance() { std::cout << "Destroyed handler instance" << std::endl; }
    };
    std::unique_ptr<Instance> _instance = std::make_unique<Instance>();
};
int main()
{
    struct Hack : boost::asio::io_context { 
        using boost::asio::io_context::shutdown;
    } io;
    auto work = make_work_guard(io);
    std::cout << " -- run" << std::endl;
    auto t = std::thread([&]{ io.run(); });
    {
        boost::asio::high_resolution_timer tim(io, 2s);
        tim.async_wait(Handler{});
        work.reset(); // no longer needed
        std::this_thread::sleep_for(500ms);
#if 1
        io.stop();
#else
        io.shutdown(); // segfaults
#endif
    }
    std::cout << " -- timer destructed" << std::endl;
    std::cout << " -- joining" << std::endl;
    t.join();
    std::cout << " -- empy run to flush handler queue" << std::endl;
    io.reset();
    //io.run();
    //io.run_one();
    io.poll_one();
    std::cout << " -- bye" << std::endl;
}
Prints
 -- run
Created handler instance
 -- timer destructed
 -- joining
 -- empy run to flush handler queue
Handler invoked: Operation canceled
Destroyed handler instance
 -- bye
Here's my best suggestion (apart from, I guess, not sharing io at all):
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;
struct Handler {
    void operator()(boost::system::error_code ec) { std::cout << "Handler invoked: " << ec.message() << std::endl; }
    struct Instance { // logging only unique instance to avoid noise of moved handlers
        Instance()  { std::cout << "Created handler instance"   << std::endl; }
        ~Instance() { std::cout << "Destroyed handler instance" << std::endl; }
    };
    std::unique_ptr<Instance> _instance = std::make_unique<Instance>();
};
int main()
{
    std::unique_ptr<boost::asio::io_context> io;
    int i = 1;
    for (auto delay : { 1500ms, 500ms }) {
        std::cout << " ------------------- reinitialized -------------- \n";
        io = std::make_unique<boost::asio::io_context>();
        boost::asio::high_resolution_timer tim(*io, 1s);
        std::cout << i << " -- run" << std::endl;
        auto t = std::thread([&]{ io->run(); });
        tim.async_wait(Handler{});
        std::this_thread::sleep_for(delay);
        std::cout << i << " -- stop" << std::endl;
        io->stop();
        std::cout << i << " -- joining" << std::endl;
        t.join();
        std::cout << " ------------------- destruct ------------------- \n";
        io.reset();
    }
    std::cout << "Bye" << std::endl;
}
Prints
 ------------------- reinitialized -------------- 
1 -- run
Created handler instance
Handler invoked: Success
Destroyed handler instance
1 -- stop
1 -- joining
 ------------------- destruct ------------------- 
 ------------------- reinitialized -------------- 
1 -- run
Created handler instance
1 -- stop
1 -- joining
 ------------------- destruct ------------------- 
Destroyed handler instance
Bye
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With