Source code of Windows XP (NT5)
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.

513 lines
14 KiB

  1. /*++
  2. Copyright (C) 2001 Microsoft Corporation
  3. Module Name:
  4. THRPOOL.H
  5. Abstract:
  6. General purpose thread pool.
  7. History:
  8. raymcc 25-Feb-99 Created
  9. --*/
  10. /*
  11. (1) Flexible thread pool size. Starts at zero and only creates threads
  12. as required.
  13. (2) Threads wait to be reused and if not reused within the alloted time,
  14. they self-terminate. Therefore, the thread pool drops to zero
  15. within the specified timeout if no activity occurs.
  16. (3) To avoid creating a lot of threads quickly, a request will attempt
  17. to wait a small amount of time for a thread to free up before requesting
  18. a new thread creation.
  19. */
  20. #include "precomp.h"
  21. #include <stdio.h>
  22. #include <assert.h>
  23. #include "corepol.h"
  24. #include <thrpool.h>
  25. #define THREAD_STACK_SIZE 0x2000 /* 8K */
  26. //***************************************************************************
  27. //
  28. // CThreadPool::CThreadPool
  29. //
  30. //***************************************************************************
  31. // ok
  32. CThreadPool::CThreadPool(
  33. DWORD dwMsMaxIdleBeforeDie,
  34. LONG lIdleThreadLimit,
  35. LONG lMaxThreads
  36. )
  37. {
  38. m_dwMsMaxIdle = dwMsMaxIdleBeforeDie;
  39. m_lMaxThreads = lMaxThreads;
  40. m_lIdleThreadLimit = lIdleThreadLimit;
  41. m_lTotalThreads = 0;
  42. m_lIdleThreads = 0;
  43. m_hReleaseThread = CreateEvent(0, 0, 0, 0);
  44. m_hBeginRendezvous = CreateEvent(0, 0, 0, 0);
  45. m_hParmXfer = CreateEvent(0, 0, 0, 0);
  46. m_hEndRendezvous = CreateEvent(0, 0, 0, 0);
  47. InitializeCriticalSection(&m_cs_dispatch);
  48. InitializeCriticalSection(&m_cs_pool);
  49. m_bPendingRequest = false;
  50. m_bShutdown = false;
  51. m_pXferUserParms = 0;
  52. m_pXferUserProc = 0;
  53. m_pXferReturnCode = 0;
  54. m_pXferElapsedTime = 0;
  55. }
  56. //***************************************************************************
  57. //
  58. // CThreadPool::~CThreadPool
  59. //
  60. //***************************************************************************
  61. // ok
  62. CThreadPool::~CThreadPool()
  63. {
  64. bool bRes = Shutdown(); // Let all threads terminate
  65. if (bRes)
  66. {
  67. CloseHandle(m_hReleaseThread);
  68. CloseHandle(m_hBeginRendezvous);
  69. CloseHandle(m_hParmXfer);
  70. CloseHandle(m_hEndRendezvous);
  71. DeleteCriticalSection(&m_cs_dispatch);
  72. DeleteCriticalSection(&m_cs_pool);
  73. }
  74. // Else we will have to leak, since all the threads didn't shut down.
  75. }
  76. //***************************************************************************
  77. //
  78. // CThreadPool::_ThreadEntry()
  79. //
  80. // Win32 entry point, which wraps and call the per-object entry point.
  81. // We do this because Win32 cannot easily call an entry point in a C++
  82. // instance unless we use various weird calling convention override
  83. // tricks, which I don't remember right now. :)
  84. //
  85. //***************************************************************************
  86. // ok
  87. DWORD WINAPI CThreadPool::_ThreadEntry(LPVOID pArg)
  88. {
  89. CThreadPool *pThis = (CThreadPool *) pArg;
  90. pThis->Pool();
  91. return 0; // Not used
  92. }
  93. //***************************************************************************
  94. //
  95. // CThreadPool::Pool
  96. //
  97. // The basic pool is implemented here. All active threads remain in this
  98. // loop. Any threads which get too old waiting for work are allowed to
  99. // retire. New threads are created from the DispatchThread() member.
  100. //
  101. // Note that the <m_lTotalThreads> and <m_lIdleThreads> are not part
  102. // of the algorithm; they are simply there for statistical purposes.
  103. //
  104. //***************************************************************************
  105. // ok
  106. void CThreadPool::Pool()
  107. {
  108. InterlockedIncrement(&m_lTotalThreads);
  109. // Pool.
  110. // =====
  111. for (;;)
  112. {
  113. if (m_bShutdown)
  114. break;
  115. if (m_lIdleThreadLimit != -1 && m_lIdleThreads > m_lIdleThreadLimit)
  116. break;
  117. // Wait for either the thread to be idle or else until a new
  118. // request causes it to be released.
  119. // =========================================================
  120. InterlockedIncrement(&m_lIdleThreads);
  121. DWORD dwRes = WaitForSingleObject(m_hReleaseThread, m_dwMsMaxIdle);
  122. // We don't know why the thread was released. Maybe it was tired
  123. // of waiting or maybe the user is shutting down our little operation.
  124. // ===================================================================
  125. if (dwRes == WAIT_TIMEOUT || m_bShutdown)
  126. {
  127. InterlockedDecrement(&m_lIdleThreads);
  128. break;
  129. }
  130. // If here, we now enter the pool critical section and attempt
  131. // to enter into a synchronized rendezvous with the dispacher.
  132. // ============================================================
  133. EnterCriticalSection(&m_cs_pool);
  134. if (m_bPendingRequest)
  135. {
  136. InterlockedDecrement(&m_lIdleThreads);
  137. SetEvent(m_hBeginRendezvous);
  138. WaitForSingleObject(m_hParmXfer, INFINITE);
  139. // Copy the parameters that the user specified in
  140. // DispatchThread(). We cannot be here unless
  141. // DispatchThread() signaled us.
  142. // ==============================================
  143. LPVOID pUserArg = m_pXferUserParms;
  144. LPTHREAD_START_ROUTINE pUserEntry = m_pXferUserProc;
  145. LPDWORD pElapsed = m_pXferElapsedTime;
  146. LPDWORD pRetVal = m_pXferReturnCode;
  147. HANDLE hCompleted = m_hXferThreadCompleted;
  148. m_bPendingRequest = false;
  149. // No longer in 'request' mode, so a new dispatch can occur.
  150. // Note that the user's parms are safely in local variables.
  151. // Allow further dispatches to occur.
  152. // ========================================================
  153. SetEvent(m_hEndRendezvous);
  154. LeaveCriticalSection(&m_cs_pool);
  155. // If user wants elapsed time, record start time.
  156. // ==============================================
  157. DWORD dwStart = 0;
  158. if (m_pXferElapsedTime)
  159. dwStart = GetCurrentTime();
  160. // Call user's entry point.
  161. // ========================
  162. DWORD dwResToRet = pUserEntry(pUserArg);
  163. // If user wants return value, forward it.
  164. // =======================================
  165. if (pRetVal)
  166. *pRetVal = dwResToRet;
  167. // If user wants elapsed time, record stop time
  168. // and forward elapsed time.
  169. // ============================================
  170. if (m_pXferElapsedTime)
  171. *m_pXferElapsedTime = GetCurrentTime() - dwStart;
  172. // If user wants event signaled, do it.
  173. // ====================================
  174. if (hCompleted)
  175. SetEvent(hCompleted);
  176. }
  177. // If here, somehow the event got signaled and nobody wanted
  178. // the thread. This could occur if somebody executed Shutdown()
  179. // and then Restart() before Shutdown() was finished.
  180. // Simply return to top of loop.
  181. // =============================================================
  182. else
  183. {
  184. LeaveCriticalSection(&m_cs_pool);
  185. InterlockedDecrement(&m_lIdleThreads);
  186. }
  187. }
  188. InterlockedDecrement(&m_lTotalThreads);
  189. // printf("---Thread Ending--- 0x%X\n", GetCurrentThreadId());
  190. }
  191. //***************************************************************************
  192. //
  193. // CThreadPool::Shutdown
  194. //
  195. // Alas, the pool is doomed and must be drained. Inform each thread of
  196. // its fate.
  197. //
  198. //***************************************************************************
  199. // ok
  200. bool CThreadPool::Shutdown(DWORD dwMaxWait)
  201. {
  202. m_bShutdown = true;
  203. m_bPendingRequest = false;
  204. DWORD dwStart = GetCurrentTime();
  205. while (m_lTotalThreads && m_bShutdown) // No need to protect this
  206. {
  207. SetEvent(m_hReleaseThread); // Allow thread to realize it is doomed
  208. if (GetCurrentTime() - dwStart > dwMaxWait)
  209. break;
  210. Sleep(10);
  211. }
  212. if (m_lTotalThreads == 0)
  213. return true;
  214. return false;
  215. }
  216. //***************************************************************************
  217. //
  218. // CThreadPool::Restart
  219. //
  220. // Allow a thread pool to get back into business again.
  221. //
  222. //***************************************************************************
  223. // ok
  224. bool CThreadPool::Restart()
  225. {
  226. m_bShutdown = false;
  227. return true;
  228. }
  229. //***************************************************************************
  230. //
  231. // CThreadPool::CreateNewThread
  232. //
  233. // Helper to create a new thread.
  234. //
  235. //***************************************************************************
  236. // ok
  237. bool CThreadPool::CreateNewThread()
  238. {
  239. DWORD dwId;
  240. HANDLE hThread = CreateThread(
  241. 0, // Security
  242. THREAD_STACK_SIZE, // 8k stack
  243. _ThreadEntry, // Thread proc address
  244. LPVOID(this), // Thread parm
  245. 0, // Flags
  246. &dwId
  247. );
  248. if (hThread == NULL)
  249. return false;
  250. // printf("--Created Thread 0x%X\n", dwId);
  251. CloseHandle(hThread);
  252. return true;
  253. }
  254. //***************************************************************************
  255. //
  256. // CThreadPool::DispatchThread
  257. //
  258. // Called by the user to dispatch a thread to an entry point.
  259. //
  260. // Returns:
  261. // NoError -- Success
  262. // ExceededPool -- No threads available within the timeout.
  263. // Shutdown -- Thread pool is being shut down
  264. // ThreadCreationFailure -- Couldn't create a thread.
  265. //
  266. //***************************************************************************
  267. // ok
  268. int CThreadPool::DispatchThread(
  269. IN DWORD dwMaxPreferredWait,
  270. IN DWORD dwMaxWaitBeforeFail,
  271. IN LPTHREAD_START_ROUTINE pEntry,
  272. IN LPVOID pArg,
  273. IN DWORD *pdwReturnCode,
  274. IN DWORD *pdwElapsedTime,
  275. IN HANDLE hThreadCompleted
  276. )
  277. {
  278. DWORD dwRes;
  279. // Don't allow new requests during a shutdown.
  280. // ===========================================
  281. if (m_bShutdown)
  282. return ShutdownInProgress;
  283. // Serialize all access to thread acquisition.
  284. // ===========================================
  285. EnterCriticalSection(&m_cs_dispatch);
  286. // If zero threads, create one.
  287. // ============================
  288. if (m_lTotalThreads == 0)
  289. {
  290. if (CreateNewThread() != true)
  291. {
  292. LeaveCriticalSection(&m_cs_dispatch);
  293. return ThreadCreationFailure;
  294. }
  295. }
  296. // Flag for the pool to look at to determine
  297. // if a dispatch is in progress.
  298. // =========================================
  299. m_bPendingRequest = true;
  300. // Release at least one thread. If there aren't any
  301. // available, this is harmless. Likewise, if this
  302. // releases threads which don't in fact become acquired
  303. // by this dispatch, again it is harmless.
  304. // ====================================================
  305. SetEvent(m_hReleaseThread);
  306. // We now try to rendevous with a thread within the
  307. // dwMaxPreferredWait time by waiting for a 'thready ready'
  308. // event from the thread pool. If no thread responds,
  309. // because they are busy, we will timeout.
  310. // ========================================================
  311. dwRes = WaitForSingleObject(m_hBeginRendezvous, dwMaxPreferredWait);
  312. if (dwRes == WAIT_OBJECT_0)
  313. {
  314. // We are now in a synchronized rendevous with the thread pool.
  315. // Next, make the interthread parameter transfer.
  316. // ============================================================
  317. m_pXferUserParms = pArg;
  318. m_pXferUserProc = pEntry;
  319. m_pXferElapsedTime = pdwElapsedTime;
  320. m_pXferReturnCode = pdwReturnCode;
  321. m_hXferThreadCompleted = hThreadCompleted;
  322. m_bPendingRequest = false; // No longer needed
  323. // Now, release the thread pool thread to grab the parameters.
  324. // ===========================================================
  325. SetEvent(m_hParmXfer);
  326. WaitForSingleObject(m_hEndRendezvous, INFINITE);
  327. LeaveCriticalSection(&m_cs_dispatch); // Allow more dispatches
  328. return NoError;
  329. }
  330. if (m_bShutdown)
  331. {
  332. LeaveCriticalSection(&m_cs_dispatch);
  333. return ShutdownInProgress;
  334. }
  335. // If here, we didn't acquire a thread. Let's create another one.
  336. // If, in the meantime, the event shows up anyway (and we simply
  337. // didn't wait long enough!), then we ended up with another thread.
  338. // So what? If one is good, two is better.
  339. // ================================================================
  340. if (m_lMaxThreads == -1 || (m_lTotalThreads < m_lMaxThreads))
  341. {
  342. if (CreateNewThread() != true)
  343. {
  344. LeaveCriticalSection(&m_cs_dispatch);
  345. return ThreadCreationFailure;
  346. }
  347. }
  348. if (m_bShutdown)
  349. {
  350. LeaveCriticalSection(&m_cs_dispatch);
  351. return ShutdownInProgress;
  352. }
  353. // Now, wait for that same old event, indicating we have begun
  354. // a synchronized rendevous with the thread pool.
  355. // ============================================================
  356. dwRes = WaitForSingleObject(m_hBeginRendezvous, dwMaxWaitBeforeFail);
  357. if (m_bShutdown)
  358. {
  359. LeaveCriticalSection(&m_cs_dispatch);
  360. return ShutdownInProgress;
  361. }
  362. if (dwRes == WAIT_OBJECT_0)
  363. {
  364. // We are now in a synchronized rendevous with the thread pool.
  365. // Next, make the interthread parameter transfer.
  366. // ============================================================
  367. m_pXferUserParms = pArg;
  368. m_pXferUserProc = pEntry;
  369. m_pXferElapsedTime = pdwElapsedTime;
  370. m_pXferReturnCode = pdwReturnCode;
  371. m_hXferThreadCompleted = hThreadCompleted;
  372. m_bPendingRequest = false; // No longer needed
  373. // Now, release the thread pool thread to grab the parameters.
  374. // ===========================================================
  375. SetEvent(m_hParmXfer);
  376. WaitForSingleObject(m_hEndRendezvous, INFINITE);
  377. LeaveCriticalSection(&m_cs_dispatch); // Allow more dispatches
  378. return NoError;
  379. }
  380. // If here, we simply ran out of time. If in the meantime
  381. // the event gets signaled, it will benefit any new thread coming
  382. // in looking for a request!
  383. // ==============================================================
  384. m_bPendingRequest = false; // We may be too late, but it looks good anyway.
  385. LeaveCriticalSection(&m_cs_dispatch);
  386. return ExceededPool;
  387. }