qi::Future groups

#include <qi/futuregroup.hpp>

Provides helper containers for managing qi::Future objects lifetime.

Detailed Description

Rationale

class Service
{
  // ...
public:
  void add(Data data)
  {
     // No Future is ever canceled even if Service is destroyed!
     qi::Future<PreparedData> ft = qi::async<PreparedData>(
         boost::bind(&Service::prepare, this, data));
     ft.andThenR<void>(boost::bind(&Service::process, this));
  }

private:
  PreparedData prepare(Data data);
  void process(PreparedData& data);

  // ...
};

// ...

class SomeApplication
{
  // ...
public:
  // ...

  void run(std::vector<Data> alldata)
  {
    Service service;
    while(!alldata.empty())
    {
      service.add(alldata.back()); // launches tasks
      service.pop_back();
    }
    mainLoop(); // return on exit
    // We cannot wait for service's tasks to end.
  } // service is destroyed here but its tasks have not been canceled and might still be running!

};

In this example, once mainLoop() call returns, if service‘s tasks launched in add() didn’t finish yet, they will try to acceed to a destroyed Service object.

To fix this bug we either:
  1. have to wait until all tasks are finished;
  2. just cancel the tasks when we stop using the Service object.

qi::ScopedFutureGroup helps fix the second case. This is apropriate for our example because we want to cancel tasks only when the application is destroyed.

qi::ScopedFutureGroup

qi::ScopedFuture can register cancellable futures which will be cancelled if not already finished when the qi::ScopedFutureGroup is destroyed.

We can fix the example from the Rationale section by using it this way:

#include <qi/futuregroup.hpp>

class Service
{
  // ...
  qi::ScopedFutureGroup _runningTasks; // On destruction, cancel all registered futures still running.

public:
  void add(Data data)
  {
     // Here we made sure to track the futures associated with this object.
     _runningTasks.add(qi::async(boost::bind(&Service::prepare, this, data))
                          .andThenR<void>(boost::bind(&Service::process, this)));
  }

  // If we want to explicitely cancel all tasks running without destroying the Service object.
  void cancel()
  {
    _runningTasks.cancelAll();
  }

private:
  PreparedData prepare(Data data);
  void process(PreparedData& data);
  // ...
};

In this case a Service object will automatically cancel all tasks still running associated with the futures registered using qi::ScopedFutureGroup::add.

Also:
  • The registered futures are never canceled if they have been finished first, in which case qi::ScopedFutureGroup will automatically stop tracking the future.
  • Trying to register a qi::Future which have not been setup to be cancellable will be ignored at runtime and a warning will be logged. Therefore it is possible to use qi::ScopedFutureGroup in generic template classes and algorithms if you cannot know if the futures you get are cancellable or not.

Reference

qi::ScopedFutureGroup Class Reference

Introduction

More...

#include <qi/futuregroup.hpp>

Public Functions

~ScopedFutureGroup()
template<class T>
void add(Future<T> future)
void cancelAll()
bool empty() const
size_t size() const

Types

typedef boost::container::flat_map< FutureUniqueId, boost::function< void()>> FutureCancelList

Detailed Description

Cancel a group of unfinished registered futures on destruction. Guarantees that the registered set of futures will be canceled whatever the reason of the destruction of the group. All public member functions are thread-safe unless specified.

Function Documentation

qi::ScopedFutureGroup::~ScopedFutureGroup()

Destructor, cancel all unfinished futures registered.

template<class T>

Brief:

Parameters:
  • future -- Future to register.
void qi::ScopedFutureGroup::add(Future<T> future)

Register a future to be canceled if not finished when this object is destroyed, or if cancelAll() is called. Futures finishing before cancelation will be automatically unregistered. Non-cancelable futures will be ignored.

void qi::ScopedFutureGroup::cancelAll()

Cancel all registered futures and unregister them.

bool qi::ScopedFutureGroup::empty() const

Brief:

Returns:True if there is no future registered, false otherwise.
size_t qi::ScopedFutureGroup::size() const

Brief:

Returns:Count of registered futures.