123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- #ifndef __WorkerThread_h__
- #define __WorkerThread_h__
- /////////////////////////////////////////////////////////////////////////////
- // WorkerThread.h | Declaration of the TCWorkerThread class.
- //
- #include <list>
- #include <queue>
- #include "TCThread.h"
- #include "AutoHandle.h"
- #include "AutoCriticalSection.h"
- #include "ObjectLock.h"
- /////////////////////////////////////////////////////////////////////////////
- // Type definition for a user-defined callback function that is called by the
- // implementation of TCWorkerThread. The callback function is expected to
- // release any resources associated with the specified thread arguments.
- //
- // A class derived from TCWorkerThread should override the virtual
- // TCWorkerThread::OnGetWorkItemRelProc method and return a function pointer
- // of this type. Typically, the class will specify a static class method.
- //
- // See Also: TCWorkerThread, TCWorkerThread::OnGetWorkItemRelProc,
- // TCWorkerThread::XWorkItem, TCWorkerThread_ArgumentReleaseProc
- typedef void (WINAPI *TC_WorkItemRelProc)(UINT, int, LPARAM*);
- #ifdef _DOCJET_ONLY
- ///////////////////////////////////////////////////////////////////////////
- // The TCWorkerThread_ArgumentReleaseProc function is an
- // application-defined callback function used with the TCWorkerThread
- // class. It receives notification that a queued element has finished
- // executing in the worker thread. The callback function should release
- // any resources associated with the specified arguments. The
- // TC_WorkItemRelProc type defines a pointer to this callback function.
- // TCWorkerThread_ArgumentReleaseProc is a placeholder for the
- // application-defined function name.
- //
- // Parameters:
- // idMsg - The message identifier used to identify different elements of
- // work that the TCWorkerThread-derived class has posted to the worker
- // 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 worker thread.
- //
- // See Also: TC_WorkItemRelProc, TCWorkerThread,
- // TCWorkerThread::OnGetWorkItemRelProc, TCWorkerThread::PostMessage,
- // TCWorkerThread::PostMessageV, TCWorkerThread::PostMessageEx,
- // TCWorkerThread::XWorkItem
- void WINAPI TCWorkerThread_ArgumentReleaseProc(UINT idMsg, int cParams,
- LPARAM* rgParams);
- #endif // _DOCJET_ONLY
- /////////////////////////////////////////////////////////////////////////////
- // TCWorkerThread provides a base class from which a class can inherit to
- // perform elements of work within a seperate worker 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 TCWorkerThread
- // {
- //
- // 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 TCWorkerThread
- // {
- //
- // Next, declare the overrides for the pure virtual methods of the class:
- //
- // // Overrides
- // protected:
- // virtual IUnknown* OnGetUnknown();
- // virtual TC_WorkItemRelProc OnGetWorkItemRelProc();
- // 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 worker 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 OnGetWorkItemRelProc is just as easy:
- //
- // TC_WorkItemRelProc CMyClass::OnGetWorkItemRelProc()
- // {
- // return ArgumentReleaseProc;
- // }
- //
- // Note: The OnGetWorkItemRelProc 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, OnGetWorkItemRelProc 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 OnGetWorkItemRelProc override.
- //
- // Finally, in the line of normal processing, when the derived object needs
- // to queue the element of work to the worker 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: TCWorkerThread_ArgumentReleaseProc,
- // TCWorkerThread::XWorkItem
- class TCWorkerThread
- {
- // Group=Types
- protected:
- class XWorkItem;
- // Construction / Destruction
- public:
- TCWorkerThread();
- virtual ~TCWorkerThread();
- // Disallow copy constructor
- private:
- TCWorkerThread(const TCWorkerThread&);
- // Attributes
- public:
- 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_WorkItemRelProc OnGetWorkItemRelProc() = 0;
- virtual void OnMessage(UINT idMsg, int cParams, LPARAM* rgParams) = 0;
- // Implementation
- protected:
- static unsigned WINAPI ThreadThunk(void*);
- void ThreadProc();
- void DispatchWorkItem(XWorkItem* pArgs);
- void DestroyWorkItem(XWorkItem* pArgs);
- // Group=Types
- protected:
- typedef std::queue<XWorkItem*, std::list<XWorkItem*> > XQueue;
- typedef TCObjectLock<TCAutoCriticalSection> XLockQueue;
- // Group=Data Members
- protected:
- #pragma pack(push, 4)
- /////////////////////////////////////////////////////////////////////////
- // Description: Pointer to the thread object.
- //
- // See Also: TCThread
- TCThread* m_pth;
- /////////////////////////////////////////////////////////////////////////
- // 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 worker
- // thread does not get decremented more than once by an instance. This is
- // important since the destructor calls Close.
- //
- // See Also: TCWorkerThread::destructor, TCWorkerThread::Close
- BOOL m_bClosed;
- #pragma pack(pop)
- ///////////////////////////////////////////////////////////////////////////
- // Description: An event handle used to signal when new work items are
- // available on the queue.
- //
- // See Also: TCHandle, TCAutoHandle
- TCHandle m_shevtQueueNotEmpty;
- ///////////////////////////////////////////////////////////////////////////
- // Description: An event handle used to signal when the thread should be
- // shutdown.
- //
- // See Also: TCHandle, TCAutoHandle
- TCHandle m_shevtShutdown;
- ///////////////////////////////////////////////////////////////////////////
- // Description: Controls synchronized access to the Queue collection.
- //
- // See Also: TCAutoCriticalSection
- TCAutoCriticalSection m_csQueue;
- ///////////////////////////////////////////////////////////////////////////
- // Description: The queue of work items.
- //
- // See Also: TCWorkerThread::XQueue
- XQueue m_queue;
- };
- /////////////////////////////////////////////////////////////////////////////
- // Group=Attributes
- inline bool TCWorkerThread::IsCurrentThread()
- {
- return !m_pth || GetCurrentThreadId() == m_pth->m_nThreadID;
- }
- /////////////////////////////////////////////////////////////////////////////
- #endif // !__WorkerThread_h__
|