Click here to Skip to main content
15,884,989 members
Articles / Programming Languages / C++11

Asynchronous calls in C++ and MFC using boost and tbb

Rate me:
Please Sign up or sign in to vote.
4.40/5 (5 votes)
1 Nov 2013LGPL35 min read 23.7K   477   20  
Call methods of an existing class in MFC asynchronously, while still preserving the thread affinity required by MFC and COM.

Introduction

There are a lot of applications written in MFC that could benefit greatly from using available libraries like boost and tbb, while we are trying to scale them up, making them take advantage of the multiple cores available on modern platforms.

While we are supporting, migrating, improving an MFC application we think how nice it would be, to be able to call certain methods of existing classes asynchronously, so that we can free the UI thread of some of the burden, simplify implementation of time-consuming algorithms in a single-threaded UI app, or just be able to do more.

One last introductory mention would be that we intentionally left out a lot or some could argue all of the theoretical (or worse philosophical) aspects, details, nomenclatures or design patterns paradigms, and rigors of coding, concentrating in providing something very accessible, easy to read and understand, something practical. 

Background

As soon as we try to implement  asynchronous execution, all sorts of road blocks start popping up like mushrooms after rain. MFC objects have thread affinity, they are not thread safe, some of them are using a COM object inside and insist in being called from a single threaded apartment (which most often means the main UI thread) . We need to pump messages in between execution of our calls to cater the needs of the UI, COM or Winsock (async). We need to marshal calls, package parameters and return values. We have to worry about thread safety and minimal locking between threads, catch exceptions and pass them properly back to the caller.

A whole other level of challenges come if the class we would like to run asynchronously calls back, like observable objects or COM/ActiveX objects with connection points.

Overwhelming isn't it ? How badly we wish to just be able to port this code to .Net and abuse the built-in async pattern ?

The goal of this article is to illustrate a possible method for turning an existing MFC class (with all it's constraints) from a synchronous executing class into a asynchronously executing one, without changing the implementation of that class.

One very important thing for achieving this goal would be to have the class properly designed, interfaces defined, callback interfaces if that's the case.

Having interfaces allows us to implement alternative classes that forward the actual call to the original class, while the actual execution happens in a separate background thread.

And since real life can offer it, let's say that he class we would like to execute asynchronously has methods we want to execute synchronously, methods that return results synchronously, and methods we are executing async and firing callbacks upon completion. 

Using the code

We'll have to start with an apology to the reader, as the sample application might not be a very inspired one. We had to choose between doing something that would illustrate the use of boost and tbb in implementing a mechanism or method for calling methods asynchronously and not doing anything at all. So we did something we hope you will find useful.

The code was written Visual Studio 2008 SP1 with TR1, is using boost 1.49 statically linked and tbb 4.0 from 6/13/2012. The archive should have everything needed for the code to compile in both release and debug.

We have a class called CCalculator which implements the interface called ICalculator and fires events when it needs to using the ICalculatorEvents interface it receives in the constructor:

C++
class ICalculator
{
public:
    virtual bool Start(void) = 0;
    virtual int  Add(int a, int b) = 0;
    virtual void Factorial(unsigned int a) = 0;
    virtual void Stop(void) = 0;
};  
class ICalculatorEvents
{
public:
    virtual void Started(void) = 0;
    virtual void Stopped(void) = 0;
    virtual void Result(int) = 0;
    virtual void Error(int) =0;
};
   

class CCalculator :
    public ICalculator
{
public:
    CCalculator(ICalculatorEvents&);
    ~CCalculator(void);
    bool Start(void);
    int  Add(int a, int b);
    void Factorial(unsigned int a);
    void Stop(void);

protected:
    bool m_running;
    ICalculatorEvents& m_callback;
};

CCalculator::CCalculator(ICalculatorEvents& callback):
m_running(false),
m_callback(callback)
{
}

CCalculator::~CCalculator(void)
{
}

bool CCalculator::Start(void)
{
    m_running = true;
    m_callback.Started();
    return true;
}

int  CCalculator::Add(int a, int b)
{
    return a+b;
}

void CCalculator::Factorial(unsigned int a)
{    
    int factorial = (a == 0)?0:1;

    for (unsigned int i = 1; i < a; i++)
        factorial *= i;
    
    m_callback.Result(factorial);
}

void CCalculator::Stop(void)
{
    m_running = false;
    m_callback.Stopped();
} 

We use the CCalculator class inside the CMainDlg class where we make an instance of it or the alternative implementation provided by the CAsyncCalculator. Having an interface removes the need to change the user code. 

C++
BOOL CMainDlg::OnInitDialog() {
...
    //m_calculator = boost::shared_ptr<ICalculator>(new CCalculator(*this));
    m_calculator = boost::shared_ptr<ICalculator>(new CAsyncCalculator(*this));
    return TRUE;  // return TRUE  unless you set the focus to a control
} 

The alternative implementation of ICalculator interface, the one that executes the methods and the callbacks asynchronously is called CAsyncCalculator defined as follows:

C++
typedef tbb::concurrent_queue<boost::function<void()>> CallQueue;
class CEventReceiver:public CWnd
{
public:
    CEventReceiver(CallQueue& queue);
    ~CEventReceiver();
    void QueueCall(boost::function<void()> &call);
protected:
    LRESULT OnExecuteCall(WPARAM, LPARAM);
    DECLARE_MESSAGE_MAP()
private:
    CallQueue& m_call_queue;
};

class CAsyncCalculator :
    public CWinThread,
    // Is an ICalculator
    public ICalculator,
    // Implemented in terms of ...
    private ICalculatorEvents
{
public:
    CAsyncCalculator(ICalculatorEvents&);
    ~CAsyncCalculator(void);
    // ICalculator
    bool Start(void);
    int  Add(int a, int b);
    void Factorial(unsigned int a);
    void Stop(void);
protected:
    afx_msg void OnExecuteCall(WPARAM, LPARAM);
    DECLARE_MESSAGE_MAP()
    void QueueCall(boost::function<void()> &call);
private:
    // CWinThread overrides
    BOOL InitInstance();
    int ExitInstance();
    // ICalculatorEvents
    void Started(void);
    void Stopped(void);
    void Result(int);
    void Error(int);
private:
    ICalculatorEvents& m_external_callback;
    boost::shared_ptr <ICalculator> m_calculator_impl;
    CallQueue m_call_queue;
    CallQueue m_callback_queue;
    boost::barrier m_worker_thread_started;    
    CEventReceiver m_event_receiver;
};   

Points of Interest

 Before I forget, let me say that when you use multiple inheritance in MFC, you better put the MFC class as the first one, if you don't want any surprises: 

warning C4407: cast between different pointer to member representations, compiler may generate incorrect code 

While compiling this line: 

ON_THREAD_MESSAGE(UWM_EXECUTE_CALL, OnExecuteCall) 

CAsyncCalculator is a class derived from the ICalculator interface. We also derive it from CWinThread and Create a background thread in the constructor. This thread will execute our calls. We have a concurrent queue for the calls we need to execute on the background thread and another one for the callbacks. The callbacks need to be executed by the same thread that is calling the methods of the ICalculator interface, the same one that created the instance of CAsyncCalculator. This is where the CEventReceiver window class comes into picture, it is a message handling window created in the constructor of CAsyncCalculator that takes the callbacks and executes them.  

C++
CAsyncCalculator::CAsyncCalculator(ICalculatorEvents& callback):
m_external_callback(callback),
m_worker_thread_started(2),
m_event_receiver(m_callback_queue)
{
    BOOL thread_created = CreateThread();
    m_worker_thread_started.wait();    
} 

We admit that this CAsyncCalculator class is a bit jammed with too many things and it does have methods that are called from two different threads, but please take it as an educational example rather than something you might want to submit for a rigorous OOP code review. 

It is important to have the instance of the contained CCalculator object created by the background thread, where all the calls to the CCalculator instance are going to be executed to, and all the callbacks are going to be fired from.

C++
BOOL CAsyncCalculator::InitInstance()
{
    m_calculator_impl = boost::shared_ptr<ICalculator>(new CCalculator(*this));
    m_worker_thread_started.wait();
    return TRUE;
}

The implementation of ICalculator interface in CAsyncCalculator class reveals the mechanisms through which the calls are packaged and marshaled, queued to be executed on a different thread.

C++
bool CAsyncCalculator::Start(void)
{
    boost::packaged_task<bool> task(boost::bind(&ICalculator::Start,m_calculator_impl.get()));    
    boost::unique_future<bool> f = task.get_future();
    QueueCall(MoveTaskIntoFunction<bool>(task));
    
    // Timed wait
    if (!f.timed_wait(CALL_TIMEOUT))
    {
        throw std::runtime_error("Start call timeout.");
    }
    return f.get();
} 

 Above you see that we want to execute the call in a sync fashion but have a timeout as a maximum duration we afford to wait for the execution to complete. 

MoveTaskIntoFunction is a whole story in itself and we credit D Drmmr for coming up with it. You can read more about the subject here. You needed because you want to store in the same concurrent queue tasks/functions that have different return types. 

QueueCall and OnExecuteCall looked like this: 

void CAsyncCalculator::QueueCall(boost::function<void()> &call)
{
    m_call_queue.push(call);
    PostThreadMessage(UWM_EXECUTE_CALL, 0, 0);
}
void CAsyncCalculator::OnExecuteCall(WPARAM, LPARAM)
{
    boost::function<void()> call;
    while(m_call_queue.try_pop(call))
    {        
        call();
    }
} 

The callbacks are going through a similar hijacking process where the initial callback  interface reference is being stored as a member variable m_external_callback locally and replaced with a a private implementation provided by the inheritance of the ICalculatorEvents

Once the callback makes it from CCalculator to CAsyncCalculator is being packaged and queued for execution by the CEventReceiver object. 

C++
void CAsyncCalculator::Started(void)
{
    boost::packaged_task<void> task(boost::bind(&ICalculatorEvents::Started, &m_external_callback));
    m_event_receiver.QueueCall(MoveTaskIntoFunction<void>(task));
} 

This happens in the OnExecuteCall message handling method similarly with what we had in CAsyncCalculator:

C++
LRESULT CEventReceiver::OnExecuteCall (WPARAM, LPARAM)
{
    boost::function<void()> call;
    while(m_call_queue.try_pop(call))
    {        
        call();
    }
    return NULL;
}

void CEventReceiver::QueueCall(boost::function<void()> &call)
{
    m_call_queue.push(call);
    ::PostMessage(m_hWnd, UWM_EXECUTE_CALL, 0,0);
}

It might look like a lot of code to type, but I'm sure there could be ways to shorten that. 

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --