Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do it clear all posted tasks which already queued in a strand?

How do it clear all posted tasks which already queued in a io_service::strand? I see no similar method from boost document.

like image 698
Bryan Fok Avatar asked Mar 04 '13 07:03

Bryan Fok


2 Answers

I have yet to find a need for it, as it can be resolved correctly with properly designing the asynchronous call chains. Generally, the Boost.Asio API is carefully designed in such a way that it prevents complex applications from becoming complicated in the asynchronous flow.

If you have examined the call chains, and are absolutely certain that the effort to redesign them is a greater current and future risk than introducing the complication of clearing a strand, then there is a way to accomplish it. However, it does have the major side effect of deleting all uninvoked handlers within the strand, and its associated io_service.

When a strand is destroyed, its destructor schedules uninvoked handlers for deferred invocation on the io_service maintaining the guarantee of non-concurrency. The io_service destructor states that uninvoked handler objects that were scheduled for deferred invocation are destroyed. Thus, by controlling the lifetime of a strand and io_service, one can clear the handlers in a strand.

Here is an overly simplified example with a helper class clearable_strand.

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>

class clearable_strand
{
public:
  clearable_strand(boost::asio::io_service& main_io_service)
    : main_io_service_(main_io_service)
  {
    clear();
  }

public:
  template <typename Handler>
  void post(const Handler& handler)
  {
    // Post handler into the local strand.
    local_strand_->post(handler);

    // Local service now has work, so post its run handler to the
    // main service.
    main_io_service_.post(boost::bind(
      &boost::asio::io_service::run_one, boost::ref(local_io_service_)));
  }

  void clear()
  {
    // Destroy previous (if any).
    local_strand_     = boost::none;
    local_io_service_ = boost::none;
    // Reconstruct.
    local_io_service_ = boost::in_place();
    local_strand_     = boost::in_place(boost::ref(local_io_service_.get()));
  }

private:
  boost::asio::io_service&                 main_io_service_;
  boost::optional<boost::asio::io_service> local_io_service_;
  boost::optional<boost::asio::strand>     local_strand_;
};

To minimize the effect so that only the strand's handlers are destroyed, the class uses an internal io_service rather than attaching the strand to main io_service. When work is posted to the strand, a handler is then posted to the main io_service that will daisy chain and service the local io_service.

And its usage:

void print(unsigned int x)
{
  std::cout << x << std::endl;
}

int main()
{
  boost::asio::io_service io_service;
  io_service.post(boost::bind(&print, 1));

  clearable_strand strand(io_service);
  strand.post(boost::bind(&print, 2));
  strand.post(boost::bind(&print, 3));
  strand.clear(); // Handler 2 and 3 deleted.

  strand.post(boost::bind(&print, 4));
  io_service.run();
}

Running the program will output 1 and 4. I would like to stress that it is an overly simplified example, and providing thread-safety in a non-complicated manner to match that of boost::asio::strand can be a challenge.

like image 93
Tanner Sansbury Avatar answered Oct 19 '22 06:10

Tanner Sansbury


This isn't possible, you will need to restructure your design depending on your goals. One possibility is to leverage the prioritized handler example if you desire certain high priority handlers to run before lower priority ones.

like image 43
Sam Miller Avatar answered Oct 19 '22 05:10

Sam Miller