Using the Observer, Proxy and Command Patterns to Marshal Progress Reporting from a Background Thread
In an earlier article, Running Long-lived Tasks in a Background Thread, I talked about some of the problems associated with reporting progress from a background thread in a Win32 application.
In my Upload Wizard source code, you’ll find a better way to implement it, using three of the GoF patterns: the Observer
, Proxy
and Command
patterns.
Here’s how it all works.
The Problem
As explained on the Running Long-lived Tasks in a Background Thread page, you’ll probably use some variant of the Observer pattern, like this:
Here the Uploader object makes calls on an UploadObserver interface, which is implemented by the progress dialog (here called CProgressPage).
The code would look a bit like this:
struct UploadObserver {
virtual void OnProgress(int num, int denom) = 0;
};
class CProgressPage : public CPropertyPage, public UploadObserver {
// ...
};
void Uploader::DoUpload()
{
// ...
m_pObserver->OnProgress(bytesDone, bytesTotal);
// ...
}
Since the Uploader is running on a background thread, the calls to the UploadObserver interface are also made on the background thread.
A typical response of the CProgressPage object is to update some controls on the dialog. For example, it will call CProgressCtrl::SetPos
. This resolves into a call to SendMessage
. Because Windows guarantees that a window procedure will only ever be called from the thread that originally called CreateWindow
, this call to SendMessage
will block until the UI thread processes the message.
This should be avoided for (at least) two reasons:
- The background thread will block until the UI thread processes the message. This might cause a problem if your background thread is doing something that times out.
- It’s now very easy to cause a deadlock. You’ve just introduced a (hidden) lock dependency between the background thread and the foreground thread.
PostMessage
Because Windows guarantees that a message will be handled by a window on the thread it was created on, we can easily use this to our advantage.
If the background thread, instead of calling on the observer directly, uses PostMessage
, the progress updates will arrive (as Windows messages) on the foreground thread correctly.
Unfortunately, this removes our UploadObserver
object from the picture. The background thread is posting messages, and the progress dialog is handling them directly. The whole point of using an observer was to reduce coupling between the Uploader
and the CProgressPage
, which we’ve now just reintroduced.
Using a Proxy Window
My preferred solution to this (and I’ve seen it in other places, too) is to use the Proxy pattern:
In this implementation, the Uploader
object calls methods on the UploadObserver
interface, as normal. The implementation of UploadObserver
in UploadObserverProxy
turns each of these calls into a call to PostMessage
, sending the messages to itself.
In the relevant message handler, it forwards the call to another UploadObserver
(the progress dialog). Remember that the initial call is on the background thread, and that the messages must be handled on the foreground thread.
In this way, the proxy ensures that the progress dialog sees the calls on the foreground thread.
This generally results in code that looks a bit like this:
class CProxyWindow : public CWnd, public UploadObserver {
UploadObserver *m_pOther;
// ...
virtual void OnProgress(int num, int denom)
{
PostMessage(MY_WM_PROGRESS, num, denom);
}
LRESULT OnMyProgress(WPARAM wParam, LPARAM lParam)
{
m_pOther->OnProgress(wParam, lParam);
return 0;
}
};
The CProxyWindow::OnProgress
method is called on the background thread, and it uses PostMessage
to marshal the parameters to the foreground thread.
In the foreground thread, the message is processed by CProxyWindow::OnMyProgress
, which turns it back into the relevant call to the UploadObserver
interface.
Limitations of PostMessage
This approach has some limitations, though:
- We’re restricted to passing parameters in
wParam
andlParam
. In practice, this isn’t such a big deal: we can pass pointers to structs. This can lead to confusion, though: some methods don’t need parameters, some need simple integers, some need more complicated things. We also need to be careful about the lifetime of the struct. This function will probably have exited by the time the message arrives, meaning that we can’t pass an automatic (stack) variable. - We need a message for each progress callback. In practice, we can use the same message and decide what’s happening by looking at (e.g.) wParam.
Enter the Command Pattern
The blurb for the Command pattern in the GoF book says this:
Encapsulate a request as an object, thereby letting you parameterize clients with different requests…
This is essentially what we want. We can change the proxy window to use the command pattern by doing the following:
struct Command {
virtual void Execute() = 0;
virtual ~Command() { /* nothing */ };
};
class OnProgressCommand : public Command {
UploadObserver *m_pObserver;
int m_num, m_denom;
public:
OnProgressCommand(UploadObserver *pObserver, int num, int denom)
: m_pObserver(pObserver), m_num(num), m_denom(denom)
{
}
virtual void Execute()
{
m_pObserver->OnProgress(m_num, m_denom);
}
};
class CProxyWindow : public CWnd, public UploadObserver {
UploadObserver *m_pOther;
// ...
virtual void OnProgress(int num, int denom)
{
Command *pCommand = new OnProgressCommand(m_pOther, num, denom);
PostMessage(MY_WM_OBSERVER_COMMAND, 0, pCommand);
}
LRESULT OnMyObserverCommand(WPARAM wParam, LPARAM lParam)
{
Command *pCommand = reinterpret_cast<Command *>(lParam);
pCommand->Execute();
delete pCommand;
return 0;
}
};
Here, we define an abstract class, called Command
, which defines a method, Execute
. The various derived classes will override this as appropriate. Note also that (since this is C++) we need to have a virtual destructor in the Command
class, because we’re deleting them through a base-class pointer.
The magic in the proxy now amounts to putting together the relevant command object and posting it to the foreground thread, where it is run (has Execute
called), and then deleted.
In this way, we provide a simple and consistent way to package up the parameters required by the observer methods. For example, in my upload wizard, the OnFileProgress
method has 1 string parameter and 7 integer parameters.
In this way, we have successfully combined the Observer
, Proxy
and Command
patterns to provide robust progress reporting from a background thread.
Pointer-To-Member-Function
There’s more, though. Normally a proxy method would look like this:
void CProxyWindow::OnSomething(int arg1, char *arg2)
{
class OnSomethingCommand : public Command {
int m_arg1;
CString m_arg2;
public:
OnSomethingCommand(Progress *pProgress, int arg1, char *arg2)
: m_pProgress(pProgress), m_arg1(arg1), m_arg2(arg2)
{
}
virtual void Execute() { m_pProgress->OnSomething(m_arg1, m_arg2); }
};
Command *commandObject = new OnSomethingCommand(m_pProgress, hr);
PostMessage(WM_MY_OBSERVER_COMMAND, 0, (LPARAM)commandObject);
}
As you can see, using command objects can soon get a little unwieldy. Each observer method on the proxy needs to define its own Command
-derived object, to encapsulate which method is to be called. We also need to specify each parameter multiple times:
- As parameters to this function.
- As a member variable of the command class.
- As constructor parameters.
- In the initialiser list.
- In the call to the constructor.
- In the actual call inside Execute.
It turns out that, usually, the observer methods will take no arguments, or a single string parameter, or a single integer parameter. We’d therefore expect to be able to handle the three different cases by using a VoidCommand
, a StringCommand
and a IntegerCommand
.
But, of course, how does the command object know which method to call when it arrives at the foreground thread?
Enter one of C++’s more arcane pieces of syntax (at least it was before templates were added to the language): the pointer-to-member-function operator, or ->*
to its friends.
We can define our VoidCommand
object as follows:
class VoidCommand : public Command
{
public:
typedef void (UploadObserver::*OBSERVER_VOID_FUNC) ();
VoidCommand(UploadObserver *pObserver, OBSERVER_VOID_FUNC pFunc)
: m_pObserver(pObserver), m_pFunc(pFunc)
{
}
virtual void Execute()
{
(m_pObserver->*m_pFunc)();
}
private:
UploadObserver *m_pObserver;
OBSERVER_VOID_FUNC m_pFunc;
};
The typedef
at the top of the class definition defines OBSERVER_VOID_FUNC
as being a pointer to a method that takes no arguments and returns void, and that is a member of UploadObserver
.
We can use this object as follows:
Command *commandObject =
new VoidCommand(m_pProgress,
&UploadObserver::OnReceivingResponse);
PostMessage(WM_MY_OBSERVER_COMMAND, 0,
(LPARAM)commandObject);
You can see this code in action – although some of the names are different, and it’s been factored a little differently – in my File Upload Wizard example.