Aldebaran documentation What's new in NAOqi 2.4.3?

qi::Promise, qi::Future

The promise and future concepts allow one to provide a result asynchronously. The implementor creates a promise for the result and returns the future contained in the promise. The caller can then use the result of the future later.

You can see a complete usage example at the end of this document.

Summary

Global Namespaces

Detailed Description

qi::Future

A Future provides a way to wait for and get the result of an asynchronous operation. It is the receiving end of a qi::Future - qi::Promise pair.

Future is templated by the type of the underlying value. void is permitted.

The different states of a Future

A future can be in multiple states represented by FutureState:

  • FutureState_None: The Future is not tied to a Promise, and will never change state.
  • FutureState_Running: The future is tied to a Promise, and the asynchronous operation has not finished yet.
  • FutureState_Canceled: The operation was successfully canceled.
  • FutureState_FinishedWithError: The operation finished with an error.
  • FutureState_FinishedWithValue: The operation finished and its return value is available.

Getting the state and waiting for a Future

There are multiple ways to handle a future. The first is to use the qi::Future<T>::value method. In that case, the call will block until the Future leaves the running state. Then if a value if available, it will be returned. Otherwise a FutureException will be raised:

qi::Future<int> someOperation();

int callIt()
{
  int i = someOperation().value(); // wait, then get the value or throw
  return i;
}

If you do not wish to wait forever, or want to handle Future error without catching an exception, you can use Future<T>::wait (timeout): this function waits at most the specified time in milliseconds, and return a FutureState. You can then safely call Future<T>::value or Future<T>::error, if future is in state FutureState_FinishedWithValue or FutureState_FinishedWithError respectively:

qi::Future<int> f = someOperation();
switch(f.wait(1000))
{
   case FutureState_Running:
     std::cerr << "Still not ready" << std::endl;
     break;
   case FutureState_Canceled:
     std::cerr << "Canceled" << std::endl;
     break;
   case FutureState_FinishedWithError:
     std::cerr << "Error: " << f.error() << std::endl;
     break;
   case FutureState_FinishedWithValue:
     std::cerr << "Value: " << f.value() << std::endl;
     break;
}

Future notification

Alternatively, you can get notified of Future completion asynchronously using Future<T>::connect. This function accepts a callback function or functor with signature void (qi::Future<T> f).

The Future guarantees you that your callback function will be called once and only once, when or if the Future leaves the Running state (that is, enters one of Canceled, FinishedWithError or FinishedWithValue):

void myCallback(const qi::Future<int>& f)
{
  qi::FutureState s = f.wait(); // will return immediately, Future has finished.
  switch(s) {...}
}

// ...
qi::Future<int> f = someOperation();
f.connect(&myCallback);

The callback is always invoked asynchronously (in the promise thread or in any thread, depending on the promise type) unless specified otherwise in the connect.

connect() accepts extra arguments after the callback: values or placeholders that will be bound to the call(similarly to how boost::bind works). If the first argument is a boost::weak_ptr, or inherits from qi::Trackable, then the callback will not be called if the weak_ptr cannot be locked, or if the Trackable was destroyed:

class Foo
{
public:
  void onOpFinished(const qi::Future<int>& op, int opNumber);
};

void safe_async_op(boost::shared_ptr<Foo> foo, int opNumber)
{
  qi::Future<int> future = someOperation();
  // This version will keep foo alive at least until the Future finished
  future.connect(&Foo::onOpFinished, foo, _1, opNumber);
  // This version is safe in case foo is destroyed before the Future finishes.
  future.connect(&Foo::onOpFinished, boost::weak_ptr<Foo>(foo), _1, opNumber);
}

Future cancellation

An async operation that returns a Future can support cancellation. To check if a future you have can be canceled, use isCancelable.

If isCancelable returns true, you can try to abort the operation by calling cancel. Depending on the operation and on the timing of your call, your cancel request might be ignored (for example, if it is received too late and a value is already available). But you can expect the Future to hastily leave the Running state one way or an other.

The property cancellable of a future is defined at the construction of the promise. You rarely need to check if a future is cancelable, as the specification of the function which returned it should tell whether it is cancelable or not.

qi::Promise

A qi::Promise is an object that can create and satisfy a qi::Future. Like Future, it has shared semantics (all copies of a Promise represent the same object). The next example illustrates it’s basic use case:

qi::Future<int> myFunctionReturningAFuture()
{
  qi::Promise<int> promise;
  // start an asynchronous operation, holding the promise
  // note that starting threads like that is bad practice, use qi::async
  boost::thread(someAsynchronousOp, promise);
  return promise.future();
}

void someAsynchronousOp(qi::Promise<int> promise)
{
  try {
    int result = performSomeTask();
    promise.setValue(result);
  }
  catch(const std::exception& e)
  {
    promise.setError(e.what());
  }
}

In plain English:

Supporting cancellation

If your asynchronous operation can be canceled, you must provide a callback with signature void(qi::Promise<T>) to the Promise constructor. You can provide qi::PromiseNoop if you don’t have any specific action to do upon cancellation.

This callback will then be called if a cancellation request is received by a connected Future. This callback is expected to ensure that the connected Future hastily leaves the Running state, by calling one of Promise::setValue, Promise::setError and Promise::setCanceled. However this call does not have to be made synchronously in the cancellation callback.

If you used qi::PromiseNoop, you can rely on Promise::isCancelRequested to check at a given point if the user requested a cancellation.

You can see an example with cancellation support at the end of this document.

Controlling callback execution

When one of the three state-changing functions listed above is called on a Promise, callbacks registered to the connected Future will be invoked. You can control whether this invocation is made synchronously, or asynchronously using a thread from an internal thread pool, by passing one of FutureCallbackType_Sync and FutureCallbackType_Async to the Promise constructor.

qi::FutureSync

qi::FutureSync is a lightweight wrapper on top of qi::Future that will wait on the Future in its destructor if the Future was ignored by the user.

It is intended to be used as a way to provide a default apparent synchronous-blocking behavior to a function, that can be changed into an asynchronous behavior by handling the resulting FutureSync.

Returning a FutureSync

You can simply change the returned type from Future to FutureSync in the basic example. The returned Future will transparently convert to a FutureSync.

Calling a function returning a FutureSync

FutureSync follow this simple rule: The destructor will call Future::wait from its destructor, unless:

FutureSync also has a cast operator that allows you to use the returned value transparently.

qi::FutureSync<int> someFunction();

void test()
{
  someFunction(); // will wait
  qi::FutureSync<int> f = someFunction(); // will wait at end of scope
  someFunction().async();                 // will not wait
  qi::Future<int> f2 = someFunction();    // will not wait
  someFunction().value();                 // will wait, because of value()
  int val = someFunction();               // will wait, does the same as
                                          // value(), may throw on error
}

Implementing an asynchronous function

Simple implementation

Here is an example of an asynchronous function implementation that supports cancellation.

Let’s implement this class and make calculate() asynchronous.

class Worker {
  public:
    int calculate();
};

First, calculate must return a future and we must create a function to do the actual work.

#include <qi/future.hpp>

class Worker {
  public:
    qi::Future<int> calculate();

  private:
    void doWork(qi::Promise<int> promise);
};

For the sake of this example, we’ll use a simple function to simulate work:

void Worker::doWork(qi::Promise<int> promise)
{
  int acc = 0;
  for (int i = 0; i < 100; ++i)
  {
    qi::os::msleep(10); // working...
    acc += 1;
  }
  promise.setValue(acc);
}

And then, we must call this function asynchronously and return the corresponding future:

qi::Future<int> Worker::calculate() {
  qi::Promise<int> promise;
  qi::async(boost::bind(&Worker::doWork, this, promise));
  return promise.future();
}

Now, calculate is asynchronous! But this isn’t useful at all, our code is more complex and this could have been done just by calling qi::async. What we can do now is to support cancellation so that one can call cancel() on the returned future to abort the action.

Cancellation support

Promises are cancelable when they are given a cancellation callback at construction. You usually don’t need this callback so you can just pass the no-operation callback.

qi::Future<int> Worker::calculate() {
  qi::Promise<int> promise(qi::PromiseNoop<int>);
  qi::async(boost::bind(&Worker::doWork, this, promise));
  return promise.future();
}

doWork() can now check if the future has been cancelled.

void Worker::doWork(qi::Promise<int> promise)
{
  int acc = 0;
  for (int i = 0; i < 100; ++i)
  {
    if (promise.isCancelRequested())
    {
      std::cout << "cancel requested" << std::endl;
      promise.setCanceled();
      return;
    }
    qi::os::msleep(10); // working...
    acc += 1;
  }
  promise.setValue(acc);
}

Reference

enum qi::FutureState
Name Brief
FutureState_None Future is not tied to a promise.
FutureState_Running Operation pending.
FutureState_Canceled The future has been canceled.
FutureState_FinishedWithError The operation is finished with an error.
FutureState_FinishedWithValue The operation is finished with a value.

qi::Future Class Reference

Introduction

More...

#include <qi/future.hpp>
  • Inherits: qi::detail::AddUnwrap< T >

Public Functions

Future()
Future(const Future<T>& b)
bool operator==(const Future<T>& other)
Future<T>& operator=(const Future<T>& b)
bool operator<(const Future<T>& b) const
FutureUniqueId uniqueId() const
Future(const ValueType& v, FutureCallbackType async)
const ValueType& value(int msecs) const
operator const ValueTypeCast&() const
FutureState wait(int msecs) const
FutureState wait(qi::Duration duration) const
FutureState waitFor(qi::Duration duration) const
FutureState wait(qi::SteadyClock::time_point timepoint) const
FutureState waitUntil(qi::SteadyClock::time_point timepoint) const
bool isFinished() const
bool isRunning() const
bool isCanceled() const
bool hasError(int msecs) const
bool hasValue(int msecs) const
const std::string& error(int msecs) const
FutureSync<T> sync()
void cancel()
bool isCancelable() const
template<typename R>
Future<R> thenR(FutureCallbackType type, const boost::function<R(const Future<T>&)>& func)
template<typename R>
Future<R> thenR(const boost::function<R(const Future<T>&)>& func)
template<typename R>
Future<R> andThenR(FutureCallbackType type, const boost::function<R(const typename Future<T>::ValueType&)>& func)
template<typename R>
Future<R> andThenR(const boost::function<R(const typename Future<T>::ValueType&)>& func)
boost::function<void()> makeCanceler()
template<typename AF>
void connect(const AF& fun, FutureCallbackType type)
template<typename FUNCTYPE, typename ARG0>
void connect(FUNCTYPE fun, ARG0 tracked, ..., FutureCallbackType type)
void connectWithStrand(qi::Strand* strand, const boost::function<void(const Future<T>&)>& cb)
void _connect(const boost::function<void()>& s)
boost::shared_ptr<detail::FutureBaseTyped<T>> impl()
Future(boost::shared_ptr<detail::FutureBaseTyped<T>> p)

Types

typedef detail::FutureType< T >::type ValueType
typedef detail::FutureType< T >::typecast ValueTypeCast
typedef boost::function< void(Future< T >) > Connection

Detailed Description

Class that represents a value that will be set later in time.

Function Documentation

qi::Future<T>::Future()
qi::Future<T>::Future(const Future<T>& b)
bool qi::Future<T>::operator==(const Future<T>& other)
Future<T>& qi::Future<T>::operator=(const Future<T>& b)
bool qi::Future<T>::operator<(const Future<T>& b) const
FutureUniqueId qi::Future<T>::uniqueId() const
qi::Future<T>::Future(const ValueType& v, FutureCallbackType async = FutureCallbackType_Async)

Construct a Future that already contains a value.

const ValueType& qi::Future<T>::value(int msecs = FutureTimeout_Infinite) const

Brief: Return the value associated to a Future.

Parameters:
  • msecs – timeout
Returns:

the value

This function can throw for many reason: wait timeoutuser errorfuture canceled

if an error is set, then value throw a FutureUserException, others errors are FutureException.

qi::Future<T>::operator const ValueTypeCast&() const

same as value() with an infinite timeout.

FutureState qi::Future<T>::wait(int msecs = FutureTimeout_Infinite) const

Brief:

Parameters:
  • msecs – Maximum time to wait in milliseconds, 0 means return immediately.
Returns:

a FutureState corresponding to the state of the future.

Wait for future to contain a value or an error

FutureState qi::Future<T>::wait(qi::Duration duration) const

Brief:

Parameters:
  • duration – Maximum time to wait
Returns:

a FutureState corresponding to the state of the future.

Wait for future to contain a value or an error

FutureState qi::Future<T>::waitFor(qi::Duration duration) const
FutureState qi::Future<T>::wait(qi::SteadyClock::time_point timepoint) const

Brief:

Parameters:
  • timepoint – Time until which we can wait
Returns:

a FutureState corresponding to the state of the future.

Wait for future to contain a value or an error

FutureState qi::Future<T>::waitUntil(qi::SteadyClock::time_point timepoint) const
bool qi::Future<T>::isFinished() const

Brief:

Returns:true if the future is finished do not throw
bool qi::Future<T>::isRunning() const

Brief:

Returns:true if the future is running do not throw
bool qi::Future<T>::isCanceled() const

Brief:

Returns:true if the future has been canceled This means that the future has been fully canceled, not that a cancel was requested. do not throw
bool qi::Future<T>::hasError(int msecs = FutureTimeout_Infinite) const

Brief:

Parameters:
  • msecs – timeout
Returns:

true if the future has an error. throw in the following case: timeout

bool qi::Future<T>::hasValue(int msecs = FutureTimeout_Infinite) const

Brief:

Parameters:
  • msecs – timeout
Returns:

true if the future has a value. throw in the following case: timeout

const std::string& qi::Future<T>::error(int msecs = FutureTimeout_Infinite) const

Brief:

Parameters:
  • msecs
Returns:

the error throw on timeout throw if the future do not have an actual error.

FutureSync<T> qi::Future<T>::sync()

Make the future sync Should not be useful, use wait().

void qi::Future<T>::cancel()

cancel() the asynchronous operation if possible Exact effect is controlled by the cancel implementation, but it is expected to set a value or an error to the Future as fast as possible. Note that cancelation may be asynchronous.

bool qi::Future<T>::isCancelable() const

Brief:

Returns:true if the future can be canceled. This does not mean that cancel will succeed.
template<typename R>
Future<R> qi::Future<T>::thenR(FutureCallbackType type, const boost::function<R(const Future<T>&)>& func)
template<typename R>
Future<R> qi::Future<T>::thenR(const boost::function<R(const Future<T>&)>& func)
template<typename R>
Future<R> qi::Future<T>::andThenR(FutureCallbackType type, const boost::function<R(const typename Future<T>::ValueType&)>& func)
template<typename R>
Future<R> qi::Future<T>::andThenR(const boost::function<R(const typename Future<T>::ValueType&)>& func)
boost::function<void()> qi::Future<T>::makeCanceler()

This functor will not keep the future alive, which is useful to avoid reference cycles. If the future does not exist anymore, this is a no-op.

template<typename AF>
void qi::Future<T>::connect(const AF& fun, FutureCallbackType type = FutureCallbackType_Async)

Connect a callback function that will be called once when the Future finishes (that is, switches from running to an other state).

If type is sync, connect may block and call the callback synchronously if the future is already set.

It guaranteed that your callback will be called exactly once (unless the promise is never set or the promise is reset, which is deprecated).

template<typename FUNCTYPE, typename ARG0>
void qi::Future<T>::connect(FUNCTYPE fun, ARG0 tracked, ..., FutureCallbackType type = FutureCallbackType_Async)

Connect a callback with binding and tracking support.

If the first argument is a weak_ptr or a pointer inheriting from qi::Trackable, the callback will not be called if tracked object was destroyed.

void qi::Future<T>::connectWithStrand(qi::Strand* strand, const boost::function<void(const Future<T>&)>& cb)
void qi::Future<T>::_connect(const boost::function<void()>& s)
boost::shared_ptr<detail::FutureBaseTyped<T>> qi::Future<T>::impl()
qi::Future<T>::Future(boost::shared_ptr<detail::FutureBaseTyped<T>> p)

qi::Promise Class Reference

Introduction

More...

#include <qi/future.hpp>
  • Inherited by: qi::detail::DelayedPromise< T >

Public Functions

Promise(FutureCallbackType async)
Promise(boost::function<void(qi::Promise<T>)> cancelCallback, FutureCallbackType async)
Promise(const qi::Promise<T>& rhs)
~Promise()
void setValue(const ValueType& value)
void setError(const std::string& msg)
void setCanceled()
bool isCancelRequested() const
Future<T> future() const
ValueType& value()
void trigger()
void setOnCancel(boost::function<void(qi::Promise<T>)> cancelCallback)
Promise<T>& operator=(const Promise<T>& rhs)

Types

typedef detail::FutureType< T >::type ValueType

Detailed Description

A Promise is used to create and satisfy a Future.

Function Documentation

qi::Promise<T>::Promise(FutureCallbackType async = FutureCallbackType_Async)

Brief:

Parameters:
  • async – specify how callbacks registered with Future::connect are called: synchronously from the Promise setter, or asynchronously from a thread pool.

Create a standard promise.

qi::Promise<T>::Promise(boost::function<void(qi::Promise<T>)> cancelCallback, FutureCallbackType async = FutureCallbackType_Async)

Create a canceleable promise. If Future<T>::cancel is invoked, onCancel() will be called. It is expected to call setValue(), setError() or setCanceled() as quickly as possible, but can do so in an asynchronous way.

qi::Promise<T>::Promise(const qi::Promise<T>& rhs)
qi::Promise<T>::~Promise()
void qi::Promise<T>::setValue(const ValueType& value)

notify all future that a value has been set. throw if state != running If T is void value must be 0

void qi::Promise<T>::setError(const std::string& msg)

set the error, and notify all futures throw if state != running

void qi::Promise<T>::setCanceled()

set the cancel state, and notify all futures throw if state != running

bool qi::Promise<T>::isCancelRequested() const

return true if cancel has been called on the promise (even if the cancel callback did not run yet).

Future<T> qi::Promise<T>::future() const

Get a future linked to this promise. Can be called multiple times.

ValueType& qi::Promise<T>::value()

Gives access to the underlying value for in-place modification. trigger() must be called after the value is written to trigger the promise.

void qi::Promise<T>::trigger()

Trigger the promise with the current value.

void qi::Promise<T>::setOnCancel(boost::function<void(qi::Promise<T>)> cancelCallback)

Set a cancel callback. If the cancel is requested, calls this callback immediately.

Promise<T>& qi::Promise<T>::operator=(const Promise<T>& rhs)

qi::FutureSync Class Reference

Introduction

More...

#include <qi/future.hpp>

Public Functions

FutureSync()
FutureSync(const Future<T>& b)
FutureSync(const FutureSync<T>& b)
FutureSync(const ValueType& v)
FutureSync<T>& operator=(const FutureSync<T>& b)
FutureSync<T>& operator=(const Future<T>& b)
~FutureSync()
operator Future<T>()
bool operator<(const FutureSync<T>& b) const
FutureUniqueId uniqueId() const
const ValueType& value(int msecs) const
ValueTypeCast&() const
FutureState wait(int msecs) const
FutureState wait(qi::Duration duration) const
FutureState waitFor(qi::Duration duration) const
FutureState wait(qi::SteadyClock::time_point timepoint) const
FutureState waitUntil(qi::SteadyClock::time_point timepoint) const
bool isRunning() const
bool isFinished() const
bool isCanceled() const
bool hasError(int msecs) const
bool hasValue(int msecs) const
const std::string& error(int msecs) const
void cancel()
bool isCancelable() const
void connect(const Connection& s)
void _connect(const boost::function<void()>& s)
template<typename FUNCTYPE, typename ARG0>
void connect(FUNCTYPE fun, ARG0 tracked, ...)
Future<T> async()

Types

typedef Future< T >::ValueType ValueType
typedef Future< T >::ValueTypeCast ValueTypeCast
typedef Future< T >::Connection Connection

Detailed Description

This class allow throwing on error and being synchronous when the future is not handled by the client.

This class should only be used as return type. If you want to store it, use qi::Future.

Function Documentation

qi::FutureSync<T>::FutureSync()
qi::FutureSync<T>::FutureSync(const Future<T>& b)
qi::FutureSync<T>::FutureSync(const FutureSync<T>& b)
qi::FutureSync<T>::FutureSync(const ValueType& v)
FutureSync<T>& qi::FutureSync<T>::operator=(const FutureSync<T>& b)
FutureSync<T>& qi::FutureSync<T>::operator=(const Future<T>& b)
qi::FutureSync<T>::~FutureSync()

will block until the future returns if the future is kept synchronous

qi::FutureSync<T>::operator Future<T>()
bool qi::FutureSync<T>::operator<(const FutureSync<T>& b) const
FutureUniqueId qi::FutureSync<T>::uniqueId() const
const ValueType& qi::FutureSync<T>::value(int msecs = FutureTimeout_Infinite) const
qi::FutureSync<T>::operator const typename Future<T>::ValueTypeCast&() const
FutureState qi::FutureSync<T>::wait(int msecs = FutureTimeout_Infinite) const
FutureState qi::FutureSync<T>::wait(qi::Duration duration) const
FutureState qi::FutureSync<T>::waitFor(qi::Duration duration) const
FutureState qi::FutureSync<T>::wait(qi::SteadyClock::time_point timepoint) const
FutureState qi::FutureSync<T>::waitUntil(qi::SteadyClock::time_point timepoint) const
bool qi::FutureSync<T>::isRunning() const
bool qi::FutureSync<T>::isFinished() const
bool qi::FutureSync<T>::isCanceled() const
bool qi::FutureSync<T>::hasError(int msecs = FutureTimeout_Infinite) const
bool qi::FutureSync<T>::hasValue(int msecs = FutureTimeout_Infinite) const
const std::string& qi::FutureSync<T>::error(int msecs = FutureTimeout_Infinite) const
void qi::FutureSync<T>::cancel()
bool qi::FutureSync<T>::isCancelable() const
void qi::FutureSync<T>::connect(const Connection& s)
void qi::FutureSync<T>::_connect(const boost::function<void()>& s)
template<typename FUNCTYPE, typename ARG0>
void qi::FutureSync<T>::connect(FUNCTYPE fun, ARG0 tracked, ...)

Connect a callback with binding and tracking support.

If the first argument is a weak_ptr or a pointer inheriting from qi::Trackable, the callback will not be called if tracked object was destroyed.

Future<T> qi::FutureSync<T>::async()
template<typename T>
void qi::PromiseNoop(qi::Promise<T>&)

Helper function that does nothing on future cancelation.