qi::Actor and qi::Strand

A Strand allows one to schedule asynchronous work with the guarantee that two tasks will never be executed in parallel. This is useful to avoid data races, mutexes, and deadlocks.

An Actor receives messages and processes them. Basically, it’s just an object containing a Strand so that all asynchronous work it receives will be executed sequentially.

Detailed Description

Strand Usage

A Strand works exactly as an event loop:


             
              #include <qi/strand.hpp>

qi::Strand strand(*qi::getEventLoop());
qi::Future<void> future =
    strand.asyncDelay(myfunction, qi::MilliSeconds(100));
// you can now cancel future
future.cancel();
// and/or wait for it
future.wait();

             
            

A strand’s destructor will block until all work scheduled is finished or cancelled. You must not schedule any work when the strand is being destroyed.

The event loop must not be destroyed before all strands associated to it are destroyed.

Actor Usage

To make a single threaded object, you must inherit from Actor . Then, make sure you call strand()->join() in your destructor. All work scheduled on that object will be executed on a single thread so that there will never be more than one call in parallel, and no more work will be executed after the join() call.


             
              #include <qi/actor.hpp>

class MyActor : public Actor
{
public:
  ~MyActor()
  {
    strand()->join();
  }

  void myFunction(const std::string& str);
};

// works with type-erasure
QI_REGISTER_OBJECT(MyActor, myFunction);
// works with futures
future.connect(qi::bind(&MyActor::myFunction, myActor, "my string"));
// works with signals
signal.connect(qi::bind(&MyActor::myFunction, myActor, _1));
// works with type-erased signal/property connections
myObject.connect("mySignal",
    boost::function<void(std::string)>(
      // you need the MetaCallType_Direct to have ordering guarantees
      qi::bind(&MyActor::myFunction, myActor, _1)), MetaCallType_Direct);
// works with periodic tasks
periodicTask.setCallback(qi::bind(&MyActor::myFunction, myActor, _1));
// works with async calls
qi::asyncDelay(qi::bind(&MyActor::myFunction, myActor, "my string"),
    qi::Seconds(1));

             
            

Warning

When connecting your object, do not use boost::bind . This code does not provide single threaded guarantees:


              
               // *NOT* single threaded
future.connect(boost::bind(&MyActor::myFunction, myActor, "my string"));

              
             

Calling the object directly does not provide these guarantees either:


              
               // *NOT* single threaded
myActor.myFunction("my string");

              
             

Same thing when scheduling directly on the main eventloop.


              
               // *NOT* single threaded
qi::getEventLoop()->async(boost::bind(&MyActor::myFunction, myActor,
    "my string"));

              
             

To have your calls scheduled on the strand, use qi::bind .

It is not possible to inherit both from Actor and Trackable because an Actor has the same behavior as Trackable.

You must call strand()->join() at the beginning of your destructor. This will ensure that no more function is called on your class (similar to Trackable::destroy() ). We recommend that you write your destructor in a synchronous manner, with future.value() .

Reference

qi::Strand Class Reference

Introduction

More...


              
               #include <qi/strand.hpp>

              
             
  • Inherits: noncopyable , qi::ExecutionContext

Public Functions

Strand ( )
Strand ( qi::ExecutionContext& executionContext )
~Strand ( )
void join ( )
qi::Future<void> async ( const boost::function<void()>& cb , qi::SteadyClockTimePoint tp )
qi::Future<void> async ( const boost::function<void()>& cb , qi::Duration delay )
bool isInThisContext ( )
template<typename F>
auto schedulerFor ( F&& func , boost::function<void()> onFail )

Detailed Description

Class that schedules tasks sequentially

A strand allows one to schedule work on an eventloop with the guaranty that two callback will never be called concurrently.

Methods are thread-safe except for destructor which must never be called concurrently.

Function Documentation

qi::Strand:: Strand ( )

Construct a strand that will schedule work on the default event loop.

qi::Strand:: Strand ( qi::ExecutionContext& executionContext )

Construct a strand that will schedule work on executionContext.

qi::Strand:: ~Strand ( )

Call detroy()

void qi::Strand:: join ( )

Joins the strand

This will wait for currently running tasks to finish and will drop all tasks scheduled from the moment of the call on. A strand can’t be reused after it has been join()ed.

It is safe to call this method concurrently with other methods. All the returned futures will be set to error.

qi::Future<void> qi::Strand:: async ( const boost::function<void()>& cb , qi::SteadyClockTimePoint tp )

call a callback asynchronously to be executed on tp Deprecatedsince 2.5

qi::Future<void> qi::Strand:: async ( const boost::function<void()>& cb , qi::Duration delay )

call a callback asynchronously to be executed in delay Deprecatedsince 2.5

bool qi::Strand:: isInThisContext ( )

Brief:

Returns: true if current code is running in this strand, false otherwise. If the strand is dying (destroy() has been called, returns false)
template<typename F>
auto qi::Strand:: schedulerFor ( F&& func , boost::function<void()> onFail = {} )

qi::Actor Class Reference

Introduction

More...


              
               #include <qi/actor.hpp>

              
             
  • Inherited by: qi::SignalSpy

Public Functions

( )
( )
Actor ( qi::ExecutionContext& ec )
( )
qi::Strand* strand ( ) const
template<class... Args>
( )
template<class... Args>
( )
template<class... Args>
( )
template<class... Args>
( )
void joinTasks ( )

Detailed Description

Class that represents an actor.

Inherit from this class if you want your class to be an actor (as in the actor model). This means that your class will receive “messages” and not be called. In other words, there will never be to calls to your object in parallel, they will be queued.

Function Documentation

()
()
qi::Actor:: Actor ( qi::ExecutionContext& ec )
()
qi::Strand* qi::Actor:: strand ( ) const
()
()
()
()
void qi::Actor:: joinTasks ( )