WorkerThread.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. #ifndef __WorkerThread_h__
  2. #define __WorkerThread_h__
  3. /////////////////////////////////////////////////////////////////////////////
  4. // WorkerThread.h | Declaration of the TCWorkerThread class.
  5. //
  6. #include <list>
  7. #include <queue>
  8. #include "TCThread.h"
  9. #include "AutoHandle.h"
  10. #include "AutoCriticalSection.h"
  11. #include "ObjectLock.h"
  12. /////////////////////////////////////////////////////////////////////////////
  13. // Type definition for a user-defined callback function that is called by the
  14. // implementation of TCWorkerThread. The callback function is expected to
  15. // release any resources associated with the specified thread arguments.
  16. //
  17. // A class derived from TCWorkerThread should override the virtual
  18. // TCWorkerThread::OnGetWorkItemRelProc method and return a function pointer
  19. // of this type. Typically, the class will specify a static class method.
  20. //
  21. // See Also: TCWorkerThread, TCWorkerThread::OnGetWorkItemRelProc,
  22. // TCWorkerThread::XWorkItem, TCWorkerThread_ArgumentReleaseProc
  23. typedef void (WINAPI *TC_WorkItemRelProc)(UINT, int, LPARAM*);
  24. #ifdef _DOCJET_ONLY
  25. ///////////////////////////////////////////////////////////////////////////
  26. // The TCWorkerThread_ArgumentReleaseProc function is an
  27. // application-defined callback function used with the TCWorkerThread
  28. // class. It receives notification that a queued element has finished
  29. // executing in the worker thread. The callback function should release
  30. // any resources associated with the specified arguments. The
  31. // TC_WorkItemRelProc type defines a pointer to this callback function.
  32. // TCWorkerThread_ArgumentReleaseProc is a placeholder for the
  33. // application-defined function name.
  34. //
  35. // Parameters:
  36. // idMsg - The message identifier used to identify different elements of
  37. // work that the TCWorkerThread-derived class has posted to the worker
  38. // thread. Note that this is *not* a window message and can be any UINT
  39. // value meaningful to the derived class. Usually, the derived class will
  40. // declare an enumeration in the class to define these identifiers.
  41. // cParams - The number of LPARAM arguments pointed to by the
  42. // /rgParams/ parameter.
  43. // rgParams - An array of LPARAM arguments specified when the element of
  44. // work was queued to the worker thread.
  45. //
  46. // See Also: TC_WorkItemRelProc, TCWorkerThread,
  47. // TCWorkerThread::OnGetWorkItemRelProc, TCWorkerThread::PostMessage,
  48. // TCWorkerThread::PostMessageV, TCWorkerThread::PostMessageEx,
  49. // TCWorkerThread::XWorkItem
  50. void WINAPI TCWorkerThread_ArgumentReleaseProc(UINT idMsg, int cParams,
  51. LPARAM* rgParams);
  52. #endif // _DOCJET_ONLY
  53. /////////////////////////////////////////////////////////////////////////////
  54. // TCWorkerThread provides a base class from which a class can inherit to
  55. // perform elements of work within a seperate worker thread.
  56. //
  57. // Possible uses for this class include:
  58. // + Performing delayed garbage collection.
  59. // + Performing less time-critical tasks outside of a main thread.
  60. //
  61. // To use this class, first include it as a public base in your
  62. // class declaration:
  63. //
  64. // class CMyClass : public TCWorkerThread
  65. // {
  66. //
  67. // It should rarely conflict with other base class method names, so it is
  68. // also suitable for use with multiple inheritance:
  69. //
  70. // class CMIClass :
  71. // public IUnknown,
  72. // public TCWorkerThread
  73. // {
  74. //
  75. // Next, declare the overrides for the pure virtual methods of the class:
  76. //
  77. // // Overrides
  78. // protected:
  79. // virtual IUnknown* OnGetUnknown();
  80. // virtual TC_WorkItemRelProc OnGetWorkItemRelProc();
  81. // virtual void OnMessage(UINT idMsg, int cParams, LPARAM* rgParams);
  82. //
  83. // Following that, you should declare a *static* method used to release
  84. // message arguments:
  85. //
  86. // // Implementation
  87. // protected:
  88. // static void WINAPI ArgumentReleaseProc(UINT idMsg, int cParams,
  89. // LPARAM* rgParams);
  90. //
  91. // Then, declare an unnamed enumeration in your class, naming the type (or
  92. // types) of work that your class needs to perform from the worker thread:
  93. //
  94. // // Types
  95. // protected:
  96. // enum {e_NotifyEngineer, e_NotifyManager, e_NotifyExecutive};
  97. // };
  98. //
  99. // Next, implement OnGetUnknown. If your class is not a COM object, this can
  100. // simply return NULL:
  101. //
  102. // IUnknown* CMyClass::OnGetUnknown()
  103. // {
  104. // return NULL;
  105. // }
  106. //
  107. // But, if your class *does* implement a component object, you must return
  108. // an IUnknown pointer on your class. Do *not* AddRef the interface pointer:
  109. //
  110. // IUnknown* CMIClass::OnGetUnknown()
  111. // {
  112. // // ATL objects should use GetUnknown() instead of 'this'
  113. // return static_cast<IUnknown*>(this);
  114. // }
  115. //
  116. // The implementation of OnGetWorkItemRelProc is just as easy:
  117. //
  118. // TC_WorkItemRelProc CMyClass::OnGetWorkItemRelProc()
  119. // {
  120. // return ArgumentReleaseProc;
  121. // }
  122. //
  123. // Note: The OnGetWorkItemRelProc can return NULL only if *all* the arguments
  124. // passed to the PostMessage, PostMessageV, or PostMessageEx methods do not
  125. // represent open resources. Open resources include things such as pointers,
  126. // handles, registry keys, etc. This is rarely the case, but if such
  127. // conditions do exists for your class, OnGetWorkItemRelProc can return NULL
  128. // and the ArgumentReleaseProc static callback would not need to be declared
  129. // or implemented.
  130. //
  131. // The last of the overrides is where you actually perform the work for a
  132. // queued element. Usually, however, if multiple types of work are defined,
  133. // the method will just switch, unpack the arguments, and call a function to
  134. // peform one specific type of work:
  135. //
  136. // void CMyClass::OnMessage(UINT idMsg, int cParams, LPARAM* rgParams)
  137. // {
  138. // switch (idMsg)
  139. // {
  140. // case e_NotifyEngineer:
  141. // {
  142. // assert(2 == cParams);
  143. // CBonus* psi = reinterpret_cast<CBonus*>(rgParams[0]);
  144. // LPCTSTR psz = reinterpret_cast<LPCTSTR>(rgParams[1]);
  145. // NotifyEngineer(psi, psz);
  146. // return;
  147. // }
  148. // case e_NotifyManager:
  149. // {
  150. // assert(1 == cParams);
  151. // CWeatherRpt* pwr = reinterpret_cast<CWeatherRpt*>(rgParams[0]);
  152. // NotifyManager(pwr);
  153. // return;
  154. // }
  155. // case e_NotifyExecutive:
  156. // {
  157. // assert(1 == cParams);
  158. // CBottomLine* pbl = reinterpret_cast<CBottomLine*>(rgParams[0]);
  159. // NotifyExecutive(pbl);
  160. // return;
  161. // }
  162. // }
  163. // }
  164. //
  165. // Note: If you *really* dislike switch statements, you could specify a
  166. // class function pointer as the /idMsg/ parameter to the PostMessage
  167. // methods. Then, the switch statement (and the enum) could be eliminated.
  168. // Notice, though, that this approach would push the parameter unpacking down
  169. // to the individual member functions, which may also be considered more
  170. // elegant.
  171. //
  172. // void CMyClass::OnMessage(UINT idMsg, int cParams, LPARAM* rgParams)
  173. // {
  174. // // Declare a typedef for a member function pointer
  175. // typedef void (CMyClass::*WORK_PROC)(int, LPARAM*);
  176. // …
  177. // // Cast the idMsg back to a WORK_PROC
  178. // WORK_PROC pfn = (WORK_PROC)idMsg;
  179. // …
  180. // // Call the member function
  181. // (this->*pfn)(cParams, rgParams);
  182. // }
  183. //
  184. // Finally, the static method needs to be implemented to release the memory
  185. // used by the parameters:
  186. //
  187. // void WINAPI CMyClass::ArgumentReleaseProc(UINT idMsg, int cParams,
  188. // LPARAM* rgParams)
  189. // {
  190. // switch (idMsg)
  191. // {
  192. // case e_NotifyEngineer:
  193. // {
  194. // assert(2 == cParams);
  195. // CBonus* psi = reinterpret_cast<CBonus*>(rgParams[0]);
  196. // LPCTSTR psz = reinterpret_cast<LPCTSTR>(rgParams[1]);
  197. // delete psi;
  198. // delete [] psz;
  199. // return;
  200. // }
  201. // case e_NotifyManager:
  202. // {
  203. // assert(1 == cParams);
  204. // CWeatherRpt* pwr = reinterpret_cast<CWeatherRpt*>(rgParams[0]);
  205. // delete pwr;
  206. // return;
  207. // }
  208. // case e_NotifyExecutive:
  209. // {
  210. // assert(1 == cParams);
  211. // CBottomLine* pbl = reinterpret_cast<CBottomLine*>(rgParams[0]);
  212. // delete pbl;
  213. // return;
  214. // }
  215. // }
  216. // }
  217. //
  218. // You probably noticed that this method does most of the same things that
  219. // the virtual OnMessage method has to perform. It would seem likely that the
  220. // two operations (process and release) on the work element could be done in
  221. // the same method. A flag would indicate which operation to be performed on
  222. // the arguments once they're unpacked. This /could/ be done, but with one
  223. // minor caveat. Since the release method is static, the common function
  224. // would have to take great care to not reference any instance data or call
  225. // any non-static method. If this sounds like a reasonable tradeoff, the
  226. // static release method could be coded as follows:
  227. //
  228. // void WINAPI CMyClass::ArgumentReleaseProc(UINT idMsg, int cParams,
  229. // LPARAM* rgParams)
  230. // {
  231. // // Declare a typedef for a member function pointer
  232. // typedef void CMyClass::*WORK_PROC(int, LPARAM*);
  233. // …
  234. // // Cast the idMsg back to a WORK_PROC
  235. // WORK_PROC* pfn = (WORK_PROC*)idMsg;
  236. // …
  237. // // Create a NULL instance pointer (be careful with it!)
  238. // CMyClass* pThis = (CMyClass*)(NULL);
  239. // …
  240. // // Call the member function
  241. // (pThis->*pfn)(cParams, rgParams);
  242. // }
  243. //
  244. // For this to work correctly, the member function must *not* be virtual, and
  245. // it needs to check the *this* pointer to determine if it is to release the
  246. // arguments or to process them:
  247. //
  248. // void CMyClass::NotifyExecutive(int cParams, LPARAM* rgParams)
  249. // {
  250. // // Unpack the arguments
  251. // assert(1 == cParams);
  252. // CBottomLine* pbl = reinterpret_cast<CBottomLine*>(rgParams[0]);
  253. // …
  254. // // Delete or process the arguments
  255. // if (this)
  256. // {
  257. // // Notify the executive of the bottom line...
  258. // }
  259. // else
  260. // {
  261. // delete pbl;
  262. // }
  263. // }
  264. //
  265. // Note: For a short discussion of the reason that the release method must be
  266. // static, refer to the documentation for the OnGetWorkItemRelProc override.
  267. //
  268. // Finally, in the line of normal processing, when the derived object needs
  269. // to queue the element of work to the worker thread, a simple call to one
  270. // of the PostMessage methods is all that it takes:
  271. //
  272. // HRESULT CMyClass::ChangeBottomLine(double dRevenue, double dExpenses)
  273. // {
  274. // // Save the values, etc...
  275. // // ...
  276. // …
  277. // // Notify the executives of the change
  278. // CBottomLine* pbl = new CBottomLine(dRevenue, dExpenses);
  279. // PostMessage(e_NotifyExecutive, pbl);
  280. // …
  281. // // Indicate success
  282. // return S_OK;
  283. // }
  284. //
  285. // Of course, the PostMessage call would look slightly different if the
  286. // non-switch approach were being used:
  287. //
  288. // PostMessage(UINT(NotifyExecutive), pbl);
  289. //
  290. // ToDo: The majority of this class's functionality would be useful outside
  291. // of the singleton scenario. The argument handling and thread processing
  292. // should be moved into either a base class or another stand-alone class.
  293. //
  294. // See Also: TCWorkerThread_ArgumentReleaseProc,
  295. // TCWorkerThread::XWorkItem
  296. class TCWorkerThread
  297. {
  298. // Group=Types
  299. protected:
  300. class XWorkItem;
  301. // Construction / Destruction
  302. public:
  303. TCWorkerThread();
  304. virtual ~TCWorkerThread();
  305. // Disallow copy constructor
  306. private:
  307. TCWorkerThread(const TCWorkerThread&);
  308. // Attributes
  309. public:
  310. bool IsCurrentThread();
  311. // Operations
  312. public:
  313. void Close();
  314. void PostMessage(UINT idMsg, int cParams, ...);
  315. void PostMessageV(UINT idMsg, int cParams, va_list argptr);
  316. void PostMessageEx(UINT idMsg, int cParams, LPARAM* rgParams);
  317. // Overrides
  318. protected:
  319. virtual IUnknown* OnGetUnknown() = 0;
  320. virtual TC_WorkItemRelProc OnGetWorkItemRelProc() = 0;
  321. virtual void OnMessage(UINT idMsg, int cParams, LPARAM* rgParams) = 0;
  322. // Implementation
  323. protected:
  324. static unsigned WINAPI ThreadThunk(void*);
  325. void ThreadProc();
  326. void DispatchWorkItem(XWorkItem* pArgs);
  327. void DestroyWorkItem(XWorkItem* pArgs);
  328. // Group=Types
  329. protected:
  330. typedef std::queue<XWorkItem*, std::list<XWorkItem*> > XQueue;
  331. typedef TCObjectLock<TCAutoCriticalSection> XLockQueue;
  332. // Group=Data Members
  333. protected:
  334. #pragma pack(push, 4)
  335. /////////////////////////////////////////////////////////////////////////
  336. // Description: Pointer to the thread object.
  337. //
  338. // See Also: TCThread
  339. TCThread* m_pth;
  340. /////////////////////////////////////////////////////////////////////////
  341. // Description: A flag indicating that the Close method has been called.
  342. //
  343. // A flag indicating that the Close method has been called. This is used
  344. // by the Close method to ensure that the reference count of the worker
  345. // thread does not get decremented more than once by an instance. This is
  346. // important since the destructor calls Close.
  347. //
  348. // See Also: TCWorkerThread::destructor, TCWorkerThread::Close
  349. BOOL m_bClosed;
  350. #pragma pack(pop)
  351. ///////////////////////////////////////////////////////////////////////////
  352. // Description: An event handle used to signal when new work items are
  353. // available on the queue.
  354. //
  355. // See Also: TCHandle, TCAutoHandle
  356. TCHandle m_shevtQueueNotEmpty;
  357. ///////////////////////////////////////////////////////////////////////////
  358. // Description: An event handle used to signal when the thread should be
  359. // shutdown.
  360. //
  361. // See Also: TCHandle, TCAutoHandle
  362. TCHandle m_shevtShutdown;
  363. ///////////////////////////////////////////////////////////////////////////
  364. // Description: Controls synchronized access to the Queue collection.
  365. //
  366. // See Also: TCAutoCriticalSection
  367. TCAutoCriticalSection m_csQueue;
  368. ///////////////////////////////////////////////////////////////////////////
  369. // Description: The queue of work items.
  370. //
  371. // See Also: TCWorkerThread::XQueue
  372. XQueue m_queue;
  373. };
  374. /////////////////////////////////////////////////////////////////////////////
  375. // Group=Attributes
  376. inline bool TCWorkerThread::IsCurrentThread()
  377. {
  378. return !m_pth || GetCurrentThreadId() == m_pth->m_nThreadID;
  379. }
  380. /////////////////////////////////////////////////////////////////////////////
  381. #endif // !__WorkerThread_h__