#ifndef __UtilityThread_h__
#define __UtilityThread_h__
/////////////////////////////////////////////////////////////////////////////
// UtilityThread.h | Declaration of the TCUtilityThread class.
//
#include "TCThread.h"
/////////////////////////////////////////////////////////////////////////////
// Type definition for a user-defined callback function that is called by the
// implementation of TCUtilityThread. The callback function is expected to
// release any resources associated with the specified thread arguments.
//
// A class derived from TCUtilityThread should override the virtual
// TCUtilityThread::OnGetArgRelProc method and return a function pointer of
// this type. Typically, the class will specify a static class method.
//
// See Also: TCUtilityThread, TCUtilityThread::OnGetArgRelProc,
// TCUtilityThread::XWorkItem, TCUtilityThread_ArgumentReleaseProc
typedef void (WINAPI *TC_UtilArgRelProc)(UINT, int, LPARAM*);
#ifdef _DOCJET_ONLY
///////////////////////////////////////////////////////////////////////////
// The TCUtilityThread_ArgumentReleaseProc function is an
// application-defined callback function used with the TCUtilityThread
// class. It receives notification that a queued element has finished
// executing in the utility thread. The callback function should release
// any resources associated with the specified arguments. The
// TC_UtilArgRelProc type defines a pointer to this callback function.
// TCUtilityThread_ArgumentReleaseProc is a placeholder for the
// application-defined function name.
//
// Parameters:
// idMsg - The message identifier used to identify different elements of
// work that the TCUtilityThread-derived class has posted to the utility
// thread. Note that this is *not* a window message and can be any UINT
// value meaningful to the derived class. Usually, the derived class will
// declare an enumeration in the class to define these identifiers.
// cParams - The number of LPARAM arguments pointed to by the
// /rgParams/ parameter.
// rgParams - An array of LPARAM arguments specified when the element of
// work was queued to the utility thread.
//
// See Also: TC_UtilArgRelProc, TCUtilityThread,
// TCUtilityThread::OnGetArgRelProc, TCUtilityThread::PostMessage,
// TCUtilityThread::PostMessageV, TCUtilityThread::PostMessageEx,
// TCUtilityThread::XWorkItem
void WINAPI TCUtilityThread_ArgumentReleaseProc(UINT idMsg, int cParams,
LPARAM* rgParams);
#endif // _DOCJET_ONLY
/////////////////////////////////////////////////////////////////////////////
// TCUtilityThread provides a base class from which multiple classes can
// inherit to perform elements of work within a single, module-wide thread.
//
// Possible uses for this class include:
// + Performing delayed garbage collection.
// + Performing less time-critical tasks outside of a main thread.
//
// To use this class, first include it as a public base in your
// class declaration:
//
// class CMyClass : public TCUtilityThread
// {
//
// It should rarely conflict with other base class method names, so it is
// also suitable for use with multiple inheritance:
//
// class CMIClass :
// public IUnknown,
// public TCUtilityThread
// {
//
// Next, declare the overrides for the pure virtual methods of the class:
//
// // Overrides
// protected:
// virtual IUnknown* OnGetUnknown();
// virtual TC_UtilArgRelProc OnGetArgRelProc();
// virtual void OnMessage(UINT idMsg, int cParams, LPARAM* rgParams);
//
// Following that, you should declare a *static* method used to release
// message arguments:
//
// // Implementation
// protected:
// static void WINAPI ArgumentReleaseProc(UINT idMsg, int cParams,
// LPARAM* rgParams);
//
// Then, declare an unnamed enumeration in your class, naming the type (or
// types) of work that your class needs to perform from the utility thread:
//
// // Types
// protected:
// enum {e_NotifyEngineer, e_NotifyManager, e_NotifyExecutive};
// };
//
// Next, implement OnGetUnknown. If your class is not a COM object, this can
// simply return NULL:
//
// IUnknown* CMyClass::OnGetUnknown()
// {
// return NULL;
// }
//
// But, if your class *does* implement a component object, you must return
// an IUnknown pointer on your class. Do *not* AddRef the interface pointer:
//
// IUnknown* CMIClass::OnGetUnknown()
// {
// // ATL objects should use GetUnknown() instead of 'this'
// return static_cast<IUnknown*>(this);
// }
//
// The implementation of OnGetArgRelProc is just as easy:
//
// TC_UtilArgRelProc CMyClass::OnGetArgRelProc()
// {
// return ArgumentReleaseProc;
// }
//
// Note: The OnGetArgRelProc can return NULL only if *all* the arguments
// passed to the PostMessage, PostMessageV, or PostMessageEx methods do not
// represent open resources. Open resources include things such as pointers,
// handles, registry keys, etc. This is rarely the case, but if such
// conditions do exists for your class, OnGetArgRelProc can return NULL and
// the ArgumentReleaseProc static callback would not need to be declared or
// implemented.
//
// The last of the overrides is where you actually perform the work for a
// queued element. Usually, however, if multiple types of work are defined,
// the method will just switch, unpack the arguments, and call a function to
// peform one specific type of work:
//
// void CMyClass::OnMessage(UINT idMsg, int cParams, LPARAM* rgParams)
// {
// switch (idMsg)
// {
// case e_NotifyEngineer:
// {
// assert(2 == cParams);
// CBonus* psi = reinterpret_cast<CBonus*>(rgParams[0]);
// LPCTSTR psz = reinterpret_cast<LPCTSTR>(rgParams[1]);
// NotifyEngineer(psi, psz);
// return;
// }
// case e_NotifyManager:
// {
// assert(1 == cParams);
// CWeatherRpt* pwr = reinterpret_cast<CWeatherRpt*>(rgParams[0]);
// NotifyManager(pwr);
// return;
// }
// case e_NotifyExecutive:
// {
// assert(1 == cParams);
// CBottomLine* pbl = reinterpret_cast<CBottomLine*>(rgParams[0]);
// NotifyExecutive(pbl);
// return;
// }
// }
// }
//
// Note: If you *really* dislike switch statements, you could specify a
// class function pointer as the /idMsg/ parameter to the PostMessage
// methods. Then, the switch statement (and the enum) could be eliminated.
// Notice, though, that this approach would push the parameter unpacking down
// to the individual member functions, which may also be considered more
// elegant.
//
// void CMyClass::OnMessage(UINT idMsg, int cParams, LPARAM* rgParams)
// {
// // Declare a typedef for a member function pointer
// typedef void (CMyClass::*WORK_PROC)(int, LPARAM*);
// …
// // Cast the idMsg back to a WORK_PROC
// WORK_PROC pfn = (WORK_PROC)idMsg;
// …
// // Call the member function
// (this->*pfn)(cParams, rgParams);
// }
//
// Finally, the static method needs to be implemented to release the memory
// used by the parameters:
//
// void WINAPI CMyClass::ArgumentReleaseProc(UINT idMsg, int cParams,
// LPARAM* rgParams)
// {
// switch (idMsg)
// {
// case e_NotifyEngineer:
// {
// assert(2 == cParams);
// CBonus* psi = reinterpret_cast<CBonus*>(rgParams[0]);
// LPCTSTR psz = reinterpret_cast<LPCTSTR>(rgParams[1]);
// delete psi;
// delete [] psz;
// return;
// }
// case e_NotifyManager:
// {
// assert(1 == cParams);
// CWeatherRpt* pwr = reinterpret_cast<CWeatherRpt*>(rgParams[0]);
// delete pwr;
// return;
// }
// case e_NotifyExecutive:
// {
// assert(1 == cParams);
// CBottomLine* pbl = reinterpret_cast<CBottomLine*>(rgParams[0]);
// delete pbl;
// return;
// }
// }
// }
//
// You probably noticed that this method does most of the same things that
// the virtual OnMessage method has to perform. It would seem likely that the
// two operations (process and release) on the work element could be done in
// the same method. A flag would indicate which operation to be performed on
// the arguments once they're unpacked. This /could/ be done, but with one
// minor caveat. Since the release method is static, the common function
// would have to take great care to not reference any instance data or call
// any non-static method. If this sounds like a reasonable tradeoff, the
// static release method could be coded as follows:
//
// void WINAPI CMyClass::ArgumentReleaseProc(UINT idMsg, int cParams,
// LPARAM* rgParams)
// {
// // Declare a typedef for a member function pointer
// typedef void CMyClass::*WORK_PROC(int, LPARAM*);
// …
// // Cast the idMsg back to a WORK_PROC
// WORK_PROC* pfn = (WORK_PROC*)idMsg;
// …
// // Create a NULL instance pointer (be careful with it!)
// CMyClass* pThis = (CMyClass*)(NULL);
// …
// // Call the member function
// (pThis->*pfn)(cParams, rgParams);
// }
//
// For this to work correctly, the member function must *not* be virtual, and
// it needs to check the *this* pointer to determine if it is to release the
// arguments or to process them:
//
// void CMyClass::NotifyExecutive(int cParams, LPARAM* rgParams)
// {
// // Unpack the arguments
// assert(1 == cParams);
// CBottomLine* pbl = reinterpret_cast<CBottomLine*>(rgParams[0]);
// …
// // Delete or process the arguments
// if (this)
// {
// // Notify the executive of the bottom line...
// }
// else
// {
// delete pbl;
// }
// }
//
// Note: For a short discussion of the reason that the release method must be
// static, refer to the documentation for the OnGetArgRelProc override.
//
// Finally, in the line of normal processing, when the derived object needs
// to queue the element of work to the utility thread, a simple call to one
// of the PostMessage methods is all that it takes:
//
// HRESULT CMyClass::ChangeBottomLine(double dRevenue, double dExpenses)
// {
// // Save the values, etc...
// // ...
// …
// // Notify the executives of the change
// CBottomLine* pbl = new CBottomLine(dRevenue, dExpenses);
// PostMessage(e_NotifyExecutive, pbl);
// …
// // Indicate success
// return S_OK;
// }
//
// Of course, the PostMessage call would look slightly different if the
// non-switch approach were being used:
//
// PostMessage(UINT(NotifyExecutive), pbl);
//
// ToDo: The majority of this class's functionality would be useful outside
// of the singleton scenario. The argument handling and thread processing
// should be moved into either a base class or another stand-alone class.
//
// See Also: TCUtilityThread_ArgumentReleaseProc,
// TCUtilityThread::XWorkItem
class TCUtilityThread
{
// Group=Types
protected:
class XWorkItem;
// Construction / Destruction
public:
TCUtilityThread();
virtual ~TCUtilityThread();
// Disallow copy constructor
private:
TCUtilityThread(const TCUtilityThread&);
// Attributes
public:
static bool IsCurrentThread();
// Operations
public:
void Close();
void PostMessage(UINT idMsg, int cParams, ...);
void PostMessageV(UINT idMsg, int cParams, va_list argptr);
void PostMessageEx(UINT idMsg, int cParams, LPARAM* rgParams);
// Overrides
protected:
virtual IUnknown* OnGetUnknown() = 0;
virtual TC_UtilArgRelProc OnGetArgRelProc() = 0;
virtual void OnMessage(UINT idMsg, int cParams, LPARAM* rgParams) = 0;
// Implementation
protected:
static unsigned WINAPI ThreadProc(void*);
void DispatchWorkItem(XWorkItem* pArgs);
// Group=Enumerations
protected:
enum {wm_message = WM_APP};
// Group=Data Members
protected:
// Description: A flag indicating that the Close method has been called.
//
// A flag indicating that the Close method has been called. This is used by
// the Close method to ensure that the reference count of the utility
// thread does not get decremented more than once by an instance. This is
// important since the destructor calls Close.
//
// See Also: TCUtilityThread::destructor, TCUtilityThread::Close
bool m_bClosed;
#pragma pack(push, 4)
static long m_nRefs;
static TCThread* m_pth;
#pragma pack(pop)
};
/////////////////////////////////////////////////////////////////////////////
// Group=Attributes
inline bool TCUtilityThread::IsCurrentThread()
{
return !m_pth || GetCurrentThreadId() == m_pth->m_nThreadID;
}
/////////////////////////////////////////////////////////////////////////////
#endif // !__UtilityThread_h__