Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

402 lines
9.7 KiB

  1. /*
  2. * T H R D P O O L . C P P
  3. *
  4. * DAV Thread pool implementation
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #pragma warning(disable:4001) /* single line comments */
  9. #pragma warning(disable:4050) /* different code attributes */
  10. #pragma warning(disable:4100) /* unreferenced formal parameter */
  11. #pragma warning(disable:4115) /* named type definition in parentheses */
  12. #pragma warning(disable:4127) /* conditional expression is constant */
  13. #pragma warning(disable:4200) /* zero-sized array in struct/union */
  14. #pragma warning(disable:4201) /* nameless struct/union */
  15. #pragma warning(disable:4206) /* translation unit is empty */
  16. #pragma warning(disable:4209) /* benign typedef redefinition */
  17. #pragma warning(disable:4214) /* bit field types other than int */
  18. #pragma warning(disable:4514) /* unreferenced inline function */
  19. #pragma warning(disable:4710) /* unexpanded virtual function */
  20. #include <windows.h>
  21. #include <thrdpool.h>
  22. #include <except.h>
  23. #include <caldbg.h>
  24. #include <profile.h>
  25. #include <ex\idlethrd.h>
  26. // CDavWorkerThread ----------------------------------------------------------
  27. //
  28. class CDavWorkerThread
  29. {
  30. private:
  31. // Owning thread pool
  32. //
  33. CPoolManager& m_cpm;
  34. // Handle to completion port
  35. //
  36. HANDLE m_hCompletionPort;
  37. // Handle to worker thread
  38. //
  39. HANDLE m_hThread;
  40. // Shutdown event
  41. //
  42. HANDLE m_hShutdownEvent;
  43. // Block on GetQueuedCompletionStatus for work items
  44. //
  45. VOID GetWorkCompletion(VOID);
  46. // Thread function
  47. //
  48. static DWORD __stdcall ThreadDispatcher(PVOID pvWorkerThread);
  49. // NOT IMPLEMENTED
  50. //
  51. CDavWorkerThread(const CDavWorkerThread& p);
  52. CDavWorkerThread& operator=(const CDavWorkerThread& p);
  53. public:
  54. explicit CDavWorkerThread (CPoolManager& cpm, HANDLE h);
  55. virtual ~CDavWorkerThread();
  56. // Expose shutdown event
  57. //
  58. HANDLE QueryShutdownEvent() { return m_hShutdownEvent; }
  59. BOOL ShutdownThread() { return SetEvent (m_hShutdownEvent); }
  60. // Expose the thread handle
  61. //
  62. HANDLE QueryThread() { return m_hThread; }
  63. };
  64. // CDavWorkerThread ----------------------------------------------------------
  65. //
  66. CDavWorkerThread::CDavWorkerThread (CPoolManager& cpm, HANDLE h)
  67. : m_cpm(cpm),
  68. m_hCompletionPort(h),
  69. m_hThread(0),
  70. m_hShutdownEvent(0)
  71. {
  72. DWORD dwThreadId;
  73. // Create shutdown event
  74. //
  75. m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  76. if (m_hShutdownEvent == NULL)
  77. {
  78. DebugTrace ("Dav: WorkerThread: failed to create shutdown event: %ld", GetLastError());
  79. return;
  80. }
  81. // Create worker thread
  82. //
  83. m_hThread = CreateThread (NULL,
  84. 0,
  85. ThreadDispatcher,
  86. this,
  87. CREATE_SUSPENDED,
  88. &dwThreadId);
  89. if (m_hThread == NULL)
  90. {
  91. DebugTrace ("Dav: WorkerThread: failed to create thread: %ld", GetLastError());
  92. return;
  93. }
  94. ResumeThread (m_hThread);
  95. return;
  96. }
  97. CDavWorkerThread::~CDavWorkerThread()
  98. {
  99. CloseHandle (m_hThread);
  100. CloseHandle (m_hShutdownEvent);
  101. }
  102. DWORD __stdcall
  103. CDavWorkerThread::ThreadDispatcher (PVOID pvWorkerThread)
  104. {
  105. // Get pointer to this CWorkerThread object
  106. //
  107. CDavWorkerThread * pwt = reinterpret_cast<CDavWorkerThread *>(pvWorkerThread);
  108. //
  109. // Run the thread until we're done -- i.e. until GetWorkCompletion() returns
  110. // normally. Note that I say 'normally' here. If an exception is thrown
  111. // anywhere below GetWorkCompletion(), catch it, deal with it, and continue
  112. // execution. Don't let the thread die off.
  113. //
  114. for ( ;; )
  115. {
  116. try
  117. {
  118. //
  119. // Install our Win32 exception handler for the lifetime of the thread
  120. //
  121. CWin32ExceptionHandler win32ExceptionHandler;
  122. // If GetWorkCompletion() ever returns normally
  123. // we're done.
  124. //
  125. pwt->GetWorkCompletion();
  126. return 0;
  127. }
  128. catch ( CDAVException& )
  129. {
  130. }
  131. }
  132. }
  133. VOID
  134. CDavWorkerThread::GetWorkCompletion (void)
  135. {
  136. Assert (m_hThread);
  137. Assert (m_hCompletionPort);
  138. do
  139. {
  140. CDavWorkContext * pwc = NULL;
  141. DWORD dwBytesTransferred;
  142. LPOVERLAPPED po;
  143. DWORD dwLastError = ERROR_SUCCESS;
  144. // Wait for work items to be queued. Note that since we are using
  145. // the lpOverlapped structure, a return value of 0 from
  146. // GetQueuedCompletionStatus() does not mean the function failed.
  147. // It means the async I/O whose status is being returned failed.
  148. // We need to make the error information available to the context
  149. // so that it can do the appropriate thing.
  150. //
  151. // From the MSDN documentation:
  152. //
  153. // "If *lpOverlapped is not NULL and the function dequeues a completion
  154. // packet for a failed I/O operation from the completion port,
  155. // the return value is zero. The function stores information in the
  156. // variables pointed to by lpNumberOfBytesTransferred, lpCompletionKey,
  157. // and lpOverlapped. To get extended error information, call GetLastError."
  158. //
  159. if ( !GetQueuedCompletionStatus (m_hCompletionPort,
  160. &dwBytesTransferred,
  161. reinterpret_cast<PULONG_PTR>(&pwc),
  162. &po,
  163. INFINITE) )
  164. {
  165. dwLastError = GetLastError();
  166. // Do NOT break; See above comment.
  167. }
  168. // Check for termination packet
  169. //
  170. if (!pwc)
  171. {
  172. DebugTrace ("Dav: WorkerThread: received termination packet\n");
  173. break;
  174. }
  175. // Check for termination signal.
  176. //
  177. if (WAIT_TIMEOUT != WaitForSingleObject (m_hShutdownEvent, 0))
  178. {
  179. DebugTrace ("Dav: WorkerThread: shutdown has been signaled\n");
  180. break;
  181. }
  182. // Record the completion status data.
  183. //
  184. pwc->SetCompletionStatusData(dwBytesTransferred, dwLastError, po);
  185. // Execute the work context.
  186. //
  187. // DebugTrace ("Dav: WorkerThread: calling DwDoWork()\n"); // NOISY debug trace! -- should be tagged
  188. pwc->DwDoWork();
  189. } while (TRUE);
  190. return;
  191. }
  192. // CPoolManager --------------------------------------------------------------
  193. //
  194. BOOL
  195. CPoolManager::FInitPool (DWORD dwConcurrency)
  196. {
  197. INT i;
  198. // Create the completion port
  199. //
  200. m_hCompletionPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE,
  201. NULL,
  202. 0,
  203. dwConcurrency);
  204. if (!m_hCompletionPort.get())
  205. {
  206. DebugTrace ("Dav: thrdpool: failed to create completion port:"
  207. "GetLastError is %d",
  208. GetLastError());
  209. return FALSE;
  210. }
  211. // Create the workers
  212. //
  213. for (i = 0; i < CTHRD_WORKER; i++)
  214. m_rgpdwthrd[i] = new CDavWorkerThread (*this, m_hCompletionPort.get());
  215. return TRUE;
  216. }
  217. BOOL
  218. CPoolManager::PostWork (CDavWorkContext * pWorkContext)
  219. {
  220. Assert (Instance().m_hCompletionPort.get());
  221. // Post the work request
  222. //
  223. if (!PostQueuedCompletionStatus (Instance().m_hCompletionPort.get(),
  224. 0,
  225. (ULONG_PTR)pWorkContext,
  226. 0))
  227. {
  228. DebugTrace ("Dav: PostQCompletionStatus() failed: %d", GetLastError());
  229. return FALSE ;
  230. }
  231. return TRUE;
  232. }
  233. VOID
  234. CPoolManager::TerminateWorkers()
  235. {
  236. INT i;
  237. INT cWorkersRunning;
  238. HANDLE rgh[CTHRD_WORKER];
  239. // Kill all workers that are running
  240. //
  241. for (cWorkersRunning = 0, i = 0; i < CTHRD_WORKER; i++)
  242. {
  243. if ( m_rgpdwthrd[i] )
  244. {
  245. PostWork (NULL);
  246. rgh[cWorkersRunning++] = m_rgpdwthrd[i]->QueryThread();
  247. }
  248. }
  249. // Wait for all the threads to terminate
  250. //
  251. WaitForMultipleObjects (cWorkersRunning, rgh, TRUE, INFINITE);
  252. // Delete the worker objects, and close the handles
  253. //
  254. for (i = 0; i < CTHRD_WORKER; i++)
  255. {
  256. delete m_rgpdwthrd[i];
  257. m_rgpdwthrd[i] = 0;
  258. }
  259. }
  260. CPoolManager::~CPoolManager()
  261. {
  262. TerminateWorkers();
  263. #ifdef DBG
  264. for (INT i = 0; i < CTHRD_WORKER; i++)
  265. Assert (m_rgpdwthrd[i] == NULL);
  266. #endif // DBG
  267. }
  268. // CDavWorkContext -----------------------------------------------------------
  269. //
  270. // CDavWorkContext::~CDavWorkContext()
  271. //
  272. // Out of line virtual destructor necessary for proper deletion
  273. // of objects of derived classes via this class.
  274. //
  275. CDavWorkContext::~CDavWorkContext()
  276. {
  277. }
  278. // CIdleWorkItem -------------------------------------------------------------
  279. //
  280. class CIdleWorkItem : public IIdleThreadCallBack
  281. {
  282. //
  283. // Work context to post
  284. //
  285. CDavWorkContext * m_pWorkContext;
  286. //
  287. // Time (in milliseconds) `til when the context should be posted
  288. //
  289. DWORD m_dwMsecDelay;
  290. // NOT IMPLEMENTED
  291. //
  292. CIdleWorkItem(const CIdleWorkItem& );
  293. CIdleWorkItem& operator=(const CIdleWorkItem& );
  294. public:
  295. // CREATORS
  296. //
  297. CIdleWorkItem( CDavWorkContext * pWorkContext,
  298. DWORD dwMsecDelay ) :
  299. m_pWorkContext(pWorkContext),
  300. m_dwMsecDelay(dwMsecDelay)
  301. {
  302. }
  303. ~CIdleWorkItem() {}
  304. // ACCESSORS
  305. //
  306. DWORD DwWait()
  307. {
  308. return m_dwMsecDelay;
  309. }
  310. // MANIPULATORS
  311. //
  312. BOOL FExecute()
  313. {
  314. //
  315. // Return FALSE to remove idle work item, TRUE to keep it.
  316. // We want to remove the item if we successfully posted it
  317. // to the work queue. We want to keep it otherwise (and
  318. // presumably attempt to repost it later...)
  319. //
  320. return !CPoolManager::PostWork( m_pWorkContext );
  321. }
  322. VOID Shutdown()
  323. {
  324. //
  325. // We have no idea what the work context may be waiting for.
  326. // It could be something that would hang shutdown if it
  327. // doesn't get another chance to run. So just run it.
  328. //
  329. (void) FExecute();
  330. }
  331. };
  332. BOOL
  333. CPoolManager::PostDelayedWork (CDavWorkContext * pWorkContext,
  334. DWORD dwMsecDelay)
  335. {
  336. //
  337. //$OPT If the delay is very short and the queue length of
  338. //$OPT the worker threads is "long enough" such that the
  339. //$OPT probability that it will be at least dwMsecDelay
  340. //$OPT milliseconds before the work context is executed,
  341. //$OPT consider putting the work context directly on the
  342. //$OPT worker queues.
  343. //
  344. auto_ref_ptr<CIdleWorkItem> pIdleWorkItem(new CIdleWorkItem(pWorkContext, dwMsecDelay));
  345. return pIdleWorkItem.get() && ::FRegister( pIdleWorkItem.get() );
  346. }