Active Object with C++11

Home      KjellKod Code Page

Active Object the C++11 way

Home    KjellKod Code Page
 

 [Download]

Source code and google test example can be downloaded from GitHub

git://github.com/KjellKod/active-object.git

Or you can use the possibly old snapshot below

[2009: Updated 2011-12-03]

Introduction

When Herb Sutter wrote about Active Objects in this Effective Concurrency blog series [blog entry: Prefer Using Active Objects Instead of Naked Threads] I was thrilled since I was waiting for that particular blog post since his Effective Concurrency seminar in Kista, Stockholm back in 2009. Herb explained then and later in his blog post a  cookbook for getting your Active Object right. This could be done either with standard C++ using an object oriented approach, by passing message objects to the thread queue, or by using C++11 with lambda support and std::thread and other niceties.

Since I thought the “object oriented” approach was clumsy and would lead to lots of boilerplate code I worked on a a more clean approach that would to use the features similar to C++11 but using template magic for (Gotw83) generic callbacks and normal standard C++ (without using std::tr1::function). I showed this in my old blog with standard c++ code that can be downloaded from kjellkod.cc/active_object.

However,  just recently I made a  job move from HiQ to Prevas and with that came a week or so between assignments. This gave me a great opportunity to learn more of the new C++11 with the help of just::thread implementation of the new C++11 standard library. The result was much cleaner, more readable code with guaranteed cross-platform behavior using a mutex protected message queue for thread communication. This is a huge improvement over the sketchy “lock-free circular fifoI used in my first Active Object example.

For the new and improved Active Object I used standard library components and with the just::thread portability bundle I soon had a cross platform Active Object  that works like a charm on both Windows (Visual Studio 2010 Express) and Linux Ubuntu (g++) .

Using Git  you can download the latest snapshot of my Active Object example at git://github.com/KjellKod/active-object.git or you can download (top right) a possibly old snapshot.

The Active Object is also used in the popular and highly efficient asynchronous logger g2log. You can read about it here, and download its latest snapshot from BitBucket.


Disclaimer

This example is heavily inspired by Herb Sutter's article mentioned above. If I got it right it's all thanks to him, if I didn't then I'm sure some finer points went past me ;) Either way this is just an example of how you could do it. Feel free to use it, change it, do whatever with it. If you liked it then it's always nice to drop me a line. As usual all the code you can find here at KjellKod.cc is yours to use but comes with no guarantee whatsoever.


Code discussion regarding Active Object and std::thread creation...

It can be argued that it breaks encapsulation by using an internal static factory function instead of a class external friend factory function but to me such arguments are merely semantics. Either way the important thing here is to remember to use a factory function instead of creating and running the thread while inside the object's constructor. To do so is a bad habit in more languages than just C++.


Using a factory function which first creates the active object and then starts the thread inside is a safer way of doing it. This way it is ensured that the object is fully created before running the thread on it.


Active Object .h

Below is the updated Active Object class. The createActive() function is a helper factory function to ensure that the object is fully created before running a thread on it's run() function.

typedef std::function<void()> Callback;

class Active {
private:
    Active(const Active&) = delete;
    Active& operator=(const Active&) = delete;

    Active
(); // see: createActive()
    void doDone(){done = true;}
    void run();

    shared_queue<Callback> mq;
    std::thread thd;
    bool done; // flag for finishing
public:
    virtual ~Active();    
    void send(Callback msg_);

    // Factory: safe construction & thread start
    static std::unique_ptr<Active> createActive();
};


The main difference from my first example apart from the queue is that here I use std::function to represent the jobs in the work queue instread of a home baked encapsuled generic callback


Active Object .cpp

The implementation is very straight forward. The background thread will sleep until a producing thread puts more work onto the thread with send(...). When a new item is put onto the queue the thread is notified, wakes up and starts to process the new item(s).

At destruction a quit message is given from the destructor. This effectively makes sure that all messages are flushed and at the quit message the thread will exit.


Active::~Active() {
  Callback quit_token = std::bind(&Active::doDone, this);
  send(quit_token); // tell thread to exit
  thd_.join();
}

// Add asynchronously to queue
void Active::send(Callback msg_){
    mq.push(msg_);
}

// Will wait for msgs if queue is empty
void Active::run() {
  while (!done_) {
    // wait till job is available, then retrieve it and
    // executes the retrieved job in this thread (background)
    Callback func;
    mq_.wait_and_pop(func);
    func();
  }
}

// Factory: safe construction of object before thread start
std::unique_ptr<Active> Active::createActive(){
    std::unique_ptr<Active> aPtr(new Active());
    aPtr->thd = std::thread(&Active::run, aPtr.get());
    return aPtr;
}


How to use the Active Object: Create your Background Worker

How to use the active object in your own background worker is very straight forward. Make a "Background Worker" class that by composition has access to an active object. The Background Worker has at least one "sendJob()" or "saveData()" function that puts the job request onto a queue. Later on the job request is dealt with by the Active Object's thread.

Using the callback it can go back to your Background Worker and execute the work. All this gives a nice separation of long running tasks that are dealt with by the background thread.

Pseudo-code Example of Background Worker:

class Backgrounder {
private:
    std::unique_ptr<kjellkod::Active> active;

    // thread processing through message queue
    void bgProcessLotsOfData(std::shared_ptr<Data> msg_){
        ....//deal with the data
    }

public:
    Backgrounder(...): active(kjellkod::Active::createActive()){}
    ... 
    // Asynchronous msg API, for sending jobs for later background
    // processing by the Active Object's thread
    void saveData(const Data value_){
        using namespace kjellkod;
        std::shared_ptr<Data> ptrBg(new Data(value_));
        Callback func = std::bind(&Backgrounder::bgProcessLotsOfData, this, ptrBg);
        active->send(func); // put work onto the thread queue
    }


Summary

Above is shown a simple cookbook template that you can follow to create your own Background Worker. Just use the Active Object with the shared_queue.h and be sure to only access the private callback functions (preferably reentrant in any case) through the Active Object's thread mechanism.

Following these guidelines should help you and makes it easier to avoid lots of dangers typically involved with bare-bone raw thread programming.

Č
ċ
ď
KjellKod-active-object-c689ff9.tar.gz
(2394k)
Kjell Hedström,
Nov 8, 2011, 1:13 AM
ċ
ď
Kjell Hedström,
Nov 8, 2011, 1:13 AM
ċ
ď
active.cpp
(2k)
Kjell Hedström,
Nov 8, 2011, 1:14 AM
ċ
ď
active.h
(2k)
Kjell Hedström,
Nov 8, 2011, 1:14 AM
ċ
ď
shared_queue.h
(2k)
Kjell Hedström,
Nov 8, 2011, 1:14 AM
Comments