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.

1067 lines
24 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. tpswork.cpp
  5. Abstract:
  6. Contains Win32 thread pool services worker thread functions
  7. Contents:
  8. SHSetThreadPoolLimits
  9. SHTerminateThreadPool
  10. SHQueueUserWorkItem
  11. SHCancelUserWorkItems
  12. TerminateWorkers
  13. TpsEnter
  14. (InitializeWorkerThreadPool)
  15. (StartIOWorkerThread)
  16. (QueueIOWorkerRequest)
  17. (IOWorkerThread)
  18. (ExecuteIOWorkItem)
  19. (CThreadPool::WorkerThread)
  20. (CThreadPool::Worker)
  21. Author:
  22. Richard L Firth (rfirth) 10-Feb-1998
  23. Environment:
  24. Win32 user-mode
  25. Revision History:
  26. 10-Feb-1998 rfirth
  27. Created
  28. 12-Aug-1998 rfirth
  29. Rewritten for DEMANDTHREAD and LONGEXEC work items. Officially
  30. divergent from original which was based on NT5 base thread pool API
  31. --*/
  32. #include "priv.h"
  33. #include "threads.h"
  34. #include "tpsclass.h"
  35. #include "tpswork.h"
  36. //
  37. // private prototypes
  38. //
  39. typedef BOOL (WINAPI * t_QueueUserWorkItem)(LPTHREAD_START_ROUTINE, LPVOID, BOOL);
  40. struct WorkItem {
  41. LPTHREAD_START_ROUTINE pfnCallback;
  42. LPVOID pContext;
  43. HMODULE hModuleToFree;
  44. };
  45. DWORD
  46. InitializeWorkerThreadPool(
  47. VOID
  48. );
  49. PRIVATE
  50. DWORD
  51. StartIOWorkerThread(
  52. VOID
  53. );
  54. PRIVATE
  55. DWORD
  56. QueueIOWorkerRequest(
  57. IN LPTHREAD_START_ROUTINE pfnCallback,
  58. IN LPVOID pContext
  59. );
  60. PRIVATE
  61. VOID
  62. IOWorkerThread(
  63. IN HANDLE hEvent
  64. );
  65. PRIVATE
  66. VOID
  67. ExecuteIOWorkItem(
  68. IN CIoWorkerRequest * pPacket
  69. );
  70. PRIVATE
  71. VOID
  72. ExecuteWorkItem(
  73. IN WorkItem *pItem
  74. );
  75. //
  76. // global data
  77. //
  78. BOOL g_StartedWorkerInitialization = FALSE;
  79. BOOL g_CompletedWorkerInitialization = FALSE;
  80. BOOL g_bTpsTerminating = FALSE;
  81. DWORD g_NumIoWorkerThreads = 0;
  82. DWORD g_NumIoWorkRequests = 0;
  83. DWORD g_LastIoThreadCreationTickCount = 0;
  84. DWORD g_MaximumIoThreads = MAX_IO_WORKER_THREADS;
  85. DWORD g_MaximumIoQueueDepth = NEW_THREAD_THRESHOLD;
  86. DWORD g_ThreadCreationDelta = THREAD_CREATION_DAMPING_TIME;
  87. CDoubleLinkedList g_IoWorkerThreads;
  88. CCriticalSection_NoCtor g_IoWorkerCriticalSection;
  89. CThreadPool g_ThreadPool;
  90. DWORD g_dwWorkItemId = 0;
  91. const char g_cszShlwapi[] = "SHLWAPI.DLL";
  92. DWORD g_ActiveRequests = 0;
  93. DWORD g_dwTerminationThreadId = 0;
  94. BOOL g_bDeferredWorkerTermination = FALSE;
  95. //
  96. // functions
  97. //
  98. LWSTDAPI_(BOOL)
  99. SHSetThreadPoolLimits(
  100. IN PSH_THREAD_POOL_LIMITS pLimits
  101. )
  102. /*++
  103. Routine Description:
  104. Change internal settings
  105. Arguments:
  106. pLimits - pointer to SH_THREAD_POOL_LIMITS structure containing limits
  107. to set
  108. Return Value:
  109. BOOL
  110. Success - TRUE
  111. Failure - FALSE. See GetLastError() for more info
  112. --*/
  113. {
  114. if (!pLimits || (pLimits->dwStructSize != sizeof(SH_THREAD_POOL_LIMITS))) {
  115. return ERROR_INVALID_PARAMETER;
  116. }
  117. BOOL success = FALSE;
  118. DWORD error = ERROR_SHUTDOWN_IN_PROGRESS; // error code? look valid -justmann
  119. if (!g_bTpsTerminating) {
  120. InterlockedIncrement((LPLONG)&g_ActiveRequests);
  121. if (!g_bTpsTerminating) {
  122. error = ERROR_SUCCESS;
  123. if (!g_CompletedWorkerInitialization) {
  124. error = InitializeWorkerThreadPool();
  125. }
  126. if (error == ERROR_SUCCESS) {
  127. g_ThreadPool.SetLimits(pLimits->dwMinimumWorkerThreads,
  128. pLimits->dwMaximumWorkerThreads,
  129. pLimits->dwMaximumWorkerQueueDepth,
  130. pLimits->dwWorkerThreadIdleTimeout,
  131. pLimits->dwWorkerThreadCreationDelta
  132. );
  133. g_MaximumIoThreads = pLimits->dwMaximumIoWorkerThreads;
  134. g_MaximumIoQueueDepth = pLimits->dwMaximumIoWorkerQueueDepth;
  135. g_ThreadCreationDelta = pLimits->dwIoWorkerThreadCreationDelta;
  136. success = TRUE;
  137. }
  138. }
  139. InterlockedDecrement((LPLONG)&g_ActiveRequests);
  140. }
  141. if (success) {
  142. return success;
  143. }
  144. SetLastError(error);
  145. return success;
  146. }
  147. LWSTDAPI_(BOOL)
  148. SHTerminateThreadPool(
  149. VOID
  150. )
  151. /*++
  152. Routine Description:
  153. Required to clean up threads before unloading SHLWAPI
  154. Arguments:
  155. None.
  156. Return Value:
  157. BOOL
  158. Success - TRUE
  159. Failure - FALSE
  160. --*/
  161. {
  162. if (InterlockedExchange((PLONG)&g_bTpsTerminating, TRUE)) {
  163. return TRUE;
  164. }
  165. //
  166. // wait until all in-progress requests have finished
  167. //
  168. while (g_ActiveRequests != 0) {
  169. SleepEx(0, FALSE);
  170. }
  171. //
  172. // kill all I/O worker threads. Queued work items will be lost
  173. //
  174. TerminateWorkers();
  175. //
  176. // kill all timer threads
  177. //
  178. TerminateTimers();
  179. //
  180. // kill all wait threads
  181. //
  182. TerminateWaiters();
  183. if (!g_bDeferredWorkerTermination
  184. && !g_bDeferredTimerTermination
  185. && !g_bDeferredWaiterTermination) {
  186. g_dwTerminationThreadId = 0;
  187. g_bTpsTerminating = FALSE;
  188. } else {
  189. g_dwTerminationThreadId = GetCurrentThreadId();
  190. }
  191. return TRUE;
  192. }
  193. LWSTDAPI_(BOOL)
  194. SHQueueUserWorkItem(
  195. IN LPTHREAD_START_ROUTINE pfnCallback,
  196. IN LPVOID pContext,
  197. IN LONG lPriority,
  198. IN DWORD_PTR dwTag,
  199. OUT DWORD_PTR * pdwId OPTIONAL,
  200. IN LPCSTR pszModule OPTIONAL,
  201. IN DWORD dwFlags
  202. )
  203. /*++
  204. Routine Description:
  205. Queues a work item and associates a user-supplied tag for use by
  206. SHCancelUserWorkItems()
  207. N.B. IO work items CANNOT be cancelled due to the fact that they are
  208. queued as APCs and there is no OS support to revoke an APC
  209. Arguments:
  210. pfnCallback - caller-supplied function to call
  211. pContext - caller-supplied context argument to pfnCallback
  212. lPriority - relative priority of non-IO work item. Default is 0
  213. dwTag - caller-supplied tag for non-IO work item if TPS_TAGGEDITEM
  214. pdwId - pointer to returned ID. Pass NULL if not required. ID will be
  215. 0 for an IO work item
  216. pszModule - if specified, name of library (DLL) to load and free so that
  217. the dll will reamin in our process for the lifetime of the work
  218. item.
  219. dwFlags - flags modifying request:
  220. TPS_EXECUTEIO
  221. - execute work item in I/O thread. If set, work item
  222. cannot be tagged (and therefore cannot be subsequently
  223. cancelled) and cannot have an associated priority
  224. (both tag and priority are ignored for I/O work items)
  225. TPS_TAGGEDITEM
  226. - the dwTag field is meaningful
  227. TPS_DEMANDTHREAD
  228. - a thread will be created for this work item if one is
  229. not currently available. DEMANDTHREAD work items are
  230. queued at the head of the work queue. That is, they
  231. get the highest priority
  232. TPS_LONGEXECTIME
  233. - caller expects this work item to take relatively long
  234. time to complete (e.g. it could be in a UI loop). Work
  235. items thus described remove a thread from the pool for
  236. an indefinite amount of time
  237. Return Value:
  238. BOOL
  239. Success - TRUE
  240. Failure - FALSE. See GetLastError() for more info
  241. --*/
  242. {
  243. DWORD error;
  244. static t_QueueUserWorkItem pfQueueUserWorkItem = NULL;
  245. if (dwFlags & TPS_INVALID_FLAGS) {
  246. error = ERROR_INVALID_PARAMETER;
  247. goto exit;
  248. }
  249. error = TpsEnter();
  250. if (error != ERROR_SUCCESS) {
  251. goto exit;
  252. }
  253. if (!g_CompletedWorkerInitialization) {
  254. error = InitializeWorkerThreadPool();
  255. if (error != ERROR_SUCCESS) {
  256. goto leave;
  257. }
  258. }
  259. if (!pfQueueUserWorkItem && IsOS(OS_WHISTLERORGREATER))
  260. {
  261. HMODULE hKernel32 = GetModuleHandle("KERNEL32.DLL");
  262. if (hKernel32)
  263. {
  264. pfQueueUserWorkItem = (t_QueueUserWorkItem)GetProcAddress(hKernel32, "QueueUserWorkItem");
  265. }
  266. }
  267. if (!(dwFlags & TPS_EXECUTEIO))
  268. {
  269. if (pfQueueUserWorkItem)
  270. {
  271. // Use NT Thread pool!
  272. WorkItem *pItem = new WorkItem;
  273. if (pItem)
  274. {
  275. pItem->pfnCallback = pfnCallback;
  276. pItem->pContext = pContext;
  277. if (pszModule && *pszModule)
  278. {
  279. pItem->hModuleToFree = LoadLibrary(pszModule);
  280. }
  281. ULONG uFlags = WT_EXECUTEDEFAULT;
  282. if (dwFlags & TPS_LONGEXECTIME)
  283. uFlags |= WT_EXECUTELONGFUNCTION;
  284. error = ERROR_SUCCESS;
  285. if (!pfQueueUserWorkItem((LPTHREAD_START_ROUTINE)ExecuteWorkItem, (PVOID)pItem, uFlags))
  286. {
  287. error = GetLastError();
  288. if (pItem->hModuleToFree)
  289. FreeLibrary(pItem->hModuleToFree);
  290. delete pItem;
  291. }
  292. }
  293. else
  294. {
  295. error = ERROR_NOT_ENOUGH_MEMORY;
  296. }
  297. }
  298. else
  299. {
  300. error = g_ThreadPool.QueueWorkItem((FARPROC)pfnCallback,
  301. (ULONG_PTR)pContext,
  302. lPriority,
  303. dwTag,
  304. pdwId,
  305. pszModule,
  306. dwFlags
  307. );
  308. }
  309. }
  310. else
  311. {
  312. DWORD threshold = (g_NumIoWorkerThreads < g_MaximumIoThreads)
  313. ? (g_MaximumIoQueueDepth * g_NumIoWorkerThreads)
  314. : 0xffffffff;
  315. g_IoWorkerCriticalSection.Acquire();
  316. if ((g_NumIoWorkerThreads == 0)
  317. || ((g_NumIoWorkRequests > threshold)
  318. && (g_LastIoThreadCreationTickCount + g_ThreadCreationDelta
  319. < GetTickCount()))) {
  320. error = StartIOWorkerThread();
  321. }
  322. if (error == ERROR_SUCCESS) {
  323. error = QueueIOWorkerRequest(pfnCallback, pContext);
  324. }
  325. g_IoWorkerCriticalSection.Release();
  326. if (pdwId != NULL) {
  327. *pdwId = (DWORD_PTR)NULL;
  328. }
  329. }
  330. leave:
  331. TpsLeave();
  332. exit:
  333. BOOL success = TRUE;
  334. if (error != ERROR_SUCCESS) {
  335. SetLastError(error);
  336. success = FALSE;
  337. }
  338. return success;
  339. }
  340. LWSTDAPI_(DWORD)
  341. SHCancelUserWorkItems(
  342. IN DWORD_PTR dwTagOrId,
  343. IN BOOL bTag
  344. )
  345. /*++
  346. Routine Description:
  347. Cancels one or more queued work items. By default, if ID is supplied, only
  348. one work item can be cancelled. If tag is supplied, all work items with same
  349. tag will be deleted
  350. Arguments:
  351. dwTagOrId - user-supplied tag or API-supplied ID of work item(s) to
  352. cancel. Used as search key
  353. bTag - TRUE if dwTagOrId is tag else ID
  354. Return Value:
  355. DWORD
  356. Success - Number of work items successfully cancelled (0..0xFFFFFFFE)
  357. Failure - 0xFFFFFFFF. Use GetLastError() for more info
  358. ERROR_SHUTDOWN_IN_PROGRESS
  359. - DLL being unloaded/support terminated
  360. --*/
  361. {
  362. DWORD error = TpsEnter();
  363. DWORD result = 0xFFFFFFFF;
  364. if (error == ERROR_SUCCESS) {
  365. if (g_CompletedWorkerInitialization) {
  366. result = g_ThreadPool.RemoveTagged(dwTagOrId, bTag);
  367. }
  368. TpsLeave();
  369. }
  370. if (result != 0xFFFFFFFF) {
  371. return result;
  372. }
  373. SetLastError(error);
  374. return result;
  375. }
  376. VOID
  377. TerminateWorkers(
  378. VOID
  379. )
  380. /*++
  381. Routine Description:
  382. Terminate worker threads
  383. Arguments:
  384. None.
  385. Return Value:
  386. None.
  387. --*/
  388. {
  389. if (g_CompletedWorkerInitialization) {
  390. g_IoWorkerCriticalSection.Acquire();
  391. while (!g_IoWorkerThreads.IsEmpty()) {
  392. CIoWorkerThreadInfo * pInfo;
  393. pInfo = (CIoWorkerThreadInfo *)g_IoWorkerThreads.RemoveHead();
  394. HANDLE hThread = pInfo->GetHandle();
  395. if ((hThread != NULL) && (hThread != (HANDLE)-1)) {
  396. pInfo->SetHandle(NULL);
  397. QueueNullFunc(hThread);
  398. SleepEx(0, TRUE);
  399. }
  400. }
  401. g_IoWorkerCriticalSection.Release();
  402. g_IoWorkerCriticalSection.Terminate();
  403. //
  404. // protect ourselves against termination from within a thread pool
  405. // thread
  406. //
  407. DWORD_PTR sig = (DWORD_PTR)TlsGetValue(g_TpsTls);
  408. DWORD limit = (sig == TPS_WORKER_SIGNATURE) ? 1 : 0;
  409. g_ThreadPool.Terminate(limit);
  410. g_StartedWorkerInitialization = FALSE;
  411. g_CompletedWorkerInitialization = FALSE;
  412. g_NumIoWorkerThreads = 0;
  413. g_NumIoWorkRequests = 0;
  414. g_LastIoThreadCreationTickCount = 0;
  415. g_MaximumIoThreads = MAX_IO_WORKER_THREADS;
  416. g_MaximumIoQueueDepth = NEW_THREAD_THRESHOLD;
  417. g_ThreadCreationDelta = THREAD_CREATION_DAMPING_TIME;
  418. g_dwWorkItemId = 0;
  419. if ((sig == TPS_IO_WORKER_SIGNATURE) || (sig == TPS_WORKER_SIGNATURE)) {
  420. g_bDeferredWorkerTermination = TRUE;
  421. }
  422. }
  423. }
  424. //
  425. // private functions
  426. //
  427. PRIVATE
  428. DWORD
  429. InitializeWorkerThreadPool(
  430. VOID
  431. )
  432. /*++
  433. Routine Description:
  434. This routine initializes all aspects of the thread pool.
  435. Arguments:
  436. None.
  437. Return Value:
  438. DWORD
  439. Success - ERROR_SUCCESS
  440. Failure -
  441. --*/
  442. {
  443. DWORD error = ERROR_SUCCESS;
  444. if (!InterlockedExchange((LPLONG)&g_StartedWorkerInitialization, TRUE)) {
  445. g_IoWorkerCriticalSection.Init();
  446. g_IoWorkerThreads.Init();
  447. g_ThreadPool.Init();
  448. //
  449. // signal that initialization has completed
  450. //
  451. g_CompletedWorkerInitialization = TRUE;
  452. } else {
  453. //
  454. // relinquish the timeslice until the other thread has initialized
  455. //
  456. while (!g_CompletedWorkerInitialization) {
  457. SleepEx(0, FALSE);
  458. }
  459. }
  460. if (error == ERROR_SUCCESS) {
  461. error = g_ThreadPool.GetError();
  462. ASSERT(error == ERROR_SUCCESS);
  463. }
  464. return error;
  465. }
  466. PRIVATE
  467. DWORD
  468. StartIOWorkerThread(
  469. VOID
  470. )
  471. /*++
  472. Routine Description:
  473. This routine starts an I/O worker thread
  474. Arguments:
  475. None.
  476. Return Value:
  477. DWORD
  478. Success - ERROR_SUCCESS
  479. Failure - ERROR_NOT_ENOUGH_MEMORY
  480. --*/
  481. {
  482. HANDLE hThread;
  483. DWORD error = StartThread((LPTHREAD_START_ROUTINE)IOWorkerThread,
  484. &hThread,
  485. TRUE
  486. );
  487. if (error == ERROR_SUCCESS) {
  488. //
  489. // update the time at which the current thread was created
  490. //
  491. InterlockedExchange((LPLONG)&g_LastIoThreadCreationTickCount,
  492. (LONG)GetTickCount()
  493. );
  494. //
  495. // we have the g_IoWorkerCriticalSection. We know the CIoWorkerThreadInfo
  496. // added at the head is the one we just created
  497. //
  498. ((CIoWorkerThreadInfo *)g_IoWorkerThreads.Next())->SetHandle(hThread);
  499. //
  500. // increment the count of the thread type created
  501. //
  502. InterlockedIncrement((LPLONG)&g_NumIoWorkerThreads);
  503. } else {
  504. //
  505. // thread creation failed. If there is even one thread present do not
  506. // return failure since we can still service the work request.
  507. //
  508. if (g_NumIoWorkerThreads != 0) {
  509. error = ERROR_SUCCESS;
  510. }
  511. }
  512. return error;
  513. }
  514. PRIVATE
  515. DWORD
  516. QueueIOWorkerRequest(
  517. IN LPTHREAD_START_ROUTINE pfnCallback,
  518. IN LPVOID pContext
  519. )
  520. /*++
  521. Routine Description:
  522. This routine queues up the request to be executed in a IO worker thread.
  523. Arguments:
  524. pfnCallback - Routine that is called by the worker thread
  525. pContext - Opaque pointer passed in as an argument to pfnCallback
  526. Return Value:
  527. DWORD
  528. Success - ERROR_SUCCESS
  529. Failure - ERROR_NOT_ENOUGH_MEMORY
  530. --*/
  531. {
  532. //
  533. // since we don't have access to the additional stack parameters of
  534. // NtQueueApcThread, we must allocate a packet off the heap in which
  535. // to pass the parameters
  536. //
  537. //
  538. // PERF: use a pre-allocated cache of request packets
  539. //
  540. CIoWorkerRequest * pPacket = new CIoWorkerRequest(pfnCallback, pContext);
  541. if (pPacket == NULL) {
  542. return ERROR_NOT_ENOUGH_MEMORY;
  543. }
  544. //
  545. // increment the outstanding work request counter
  546. //
  547. InterlockedIncrement((LPLONG)&g_NumIoWorkRequests);
  548. //
  549. // in order to implement "fair" assignment of work items between IO worker
  550. // threads each time remove from head of list and reinsert at back. Keep
  551. // the pointer for thread handle
  552. //
  553. ASSERT(!g_IoWorkerThreads.IsEmpty());
  554. CIoWorkerThreadInfo * pInfo = (CIoWorkerThreadInfo *)g_IoWorkerThreads.RemoveHead();
  555. pInfo->InsertTail(&g_IoWorkerThreads);
  556. //
  557. // queue an APC to the IO worker thread. The worker thread will free the
  558. // request packet
  559. //
  560. BOOL bOk = QueueUserAPC((PAPCFUNC)ExecuteIOWorkItem,
  561. pInfo->GetHandle(),
  562. (ULONG_PTR)pPacket
  563. );
  564. DWORD error = ERROR_SUCCESS;
  565. if (!bOk) {
  566. //
  567. // GetLastError() before ASSERT()!
  568. //
  569. error = GetLastError();
  570. }
  571. return error;
  572. }
  573. PRIVATE
  574. VOID
  575. IOWorkerThread(
  576. IN HANDLE hEvent
  577. )
  578. /*++
  579. Routine Description:
  580. All I/O worker threads execute in this routine. All the work requests execute as APCs
  581. in this thread.
  582. Arguments:
  583. hEvent - event handle signalled when initialization complete
  584. Return Value:
  585. None.
  586. --*/
  587. {
  588. HMODULE hDll = LoadLibrary(g_cszShlwapi);
  589. DWORD dwExitCode = ERROR_OUTOFMEMORY;
  590. if (hDll) // LoadLibrary can fail under low-memory conditions, even though we're in shlwapi (hence it's loaded already)
  591. {
  592. ASSERT(g_TpsTls != 0xFFFFFFFF);
  593. TlsSetValue(g_TpsTls, (LPVOID)TPS_IO_WORKER_SIGNATURE);
  594. CIoWorkerThreadInfo info(g_IoWorkerThreads.Head());
  595. SetEvent(hEvent);
  596. while (!g_bTpsTerminating) {
  597. SleepEx(INFINITE, TRUE);
  598. }
  599. InterlockedDecrement((LPLONG)&g_NumIoWorkerThreads);
  600. while (info.GetHandle() != NULL) {
  601. SleepEx(0, FALSE);
  602. }
  603. if (GetCurrentThreadId() == g_dwTerminationThreadId) {
  604. g_bTpsTerminating = FALSE;
  605. g_bDeferredWorkerTermination = FALSE;
  606. g_dwTerminationThreadId = 0;
  607. }
  608. FreeLibrary(hDll);
  609. dwExitCode = ERROR_SUCCESS;
  610. }
  611. ExitThread(dwExitCode);
  612. }
  613. PRIVATE
  614. VOID
  615. ExecuteIOWorkItem(
  616. IN CIoWorkerRequest * pPacket
  617. )
  618. /*++
  619. Routine Description:
  620. Executes an IO Work function. Runs in a APC in the IO Worker thread
  621. Arguments:
  622. pPacket - pointer to CIoWorkerRequest allocated by the requesting
  623. thread. We need to free it/return it to packet cache
  624. Return Value:
  625. None.
  626. --*/
  627. {
  628. LPTHREAD_START_ROUTINE fn = pPacket->GetCallback();
  629. LPVOID ctx = pPacket->GetContext();
  630. delete pPacket;
  631. if (!g_bTpsTerminating) {
  632. fn(ctx);
  633. InterlockedDecrement((LPLONG)&g_NumIoWorkRequests);
  634. }
  635. }
  636. PRIVATE
  637. VOID
  638. ExecuteWorkItem(
  639. IN WorkItem *pItem
  640. )
  641. /*++
  642. Routine Description:
  643. Executes a regular Work function. Runs in the NT thread pool
  644. Arguments:
  645. pItem - context information. Contains the worker function that needs to
  646. be run and the hModule to free. We need to free it.
  647. Return Value:
  648. None.
  649. --*/
  650. {
  651. HMODULE hModule = pItem->hModuleToFree;
  652. LPTHREAD_START_ROUTINE pfn = pItem->pfnCallback;
  653. LPVOID ctx = pItem->pContext;
  654. delete pItem;
  655. #ifdef DEBUG
  656. HRESULT hrDebug = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
  657. if (hrDebug == RPC_E_CHANGED_MODE)
  658. {
  659. ASSERTMSG(FALSE, "SHLWAPI Thread pool wrapper: Could not CoInitialize Appartment threaded. We got infected with an MTA!\n");
  660. }
  661. else
  662. {
  663. CoUninitialize();
  664. }
  665. #endif
  666. // Do the work now
  667. pfn(ctx);
  668. #ifdef DEBUG
  669. hrDebug = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
  670. if (hrDebug == RPC_E_CHANGED_MODE)
  671. {
  672. ASSERTMSG(FALSE, "SHLWAPI Thread pool wrapper: Could not CoInitialize Appartment threaded. The task at %x forgot to CoUninitialize or "
  673. "we got infected with an MTA!\n", pfn);
  674. }
  675. else
  676. {
  677. CoUninitialize();
  678. }
  679. #endif
  680. if (hModule)
  681. FreeLibrary(hModule);
  682. }
  683. VOID
  684. CThreadPool::WorkerThread(
  685. VOID
  686. )
  687. /*++
  688. Routine Description:
  689. Static thread function. Instantiates the thread pool object pointer and
  690. calls the non-IO worker member function
  691. Arguments:
  692. None.
  693. Return Value:
  694. None.
  695. --*/
  696. {
  697. HMODULE hDll = LoadLibrary(g_cszShlwapi);
  698. DWORD dwExitCode = ERROR_OUTOFMEMORY;
  699. if (hDll) // LoadLibrary can fail under low-memory conditions, even though we're in shlwapi (hence it's loaded already)
  700. {
  701. ASSERT(g_TpsTls != 0xFFFFFFFF);
  702. TlsSetValue(g_TpsTls, (LPVOID)TPS_WORKER_SIGNATURE);
  703. g_ThreadPool.Worker();
  704. if (GetCurrentThreadId() == g_dwTerminationThreadId) {
  705. g_dwTerminationThreadId = 0;
  706. g_bDeferredWorkerTermination = FALSE;
  707. g_bTpsTerminating = FALSE;
  708. }
  709. FreeLibrary(hDll);
  710. dwExitCode = ERROR_SUCCESS;
  711. }
  712. ExitThread(dwExitCode);
  713. }
  714. VOID
  715. CThreadPool::Worker(
  716. VOID
  717. )
  718. /*++
  719. Routine Description:
  720. All non I/O worker threads execute in this routine. This function will
  721. terminate when it has not serviced a request for m_workerIdleTimeout mSec
  722. Arguments:
  723. None.
  724. Return Value:
  725. None.
  726. --*/
  727. {
  728. while (!g_bTpsTerminating)
  729. {
  730. FARPROC fn;
  731. ULONG_PTR ctx;
  732. DWORD flags;
  733. HMODULE hMouduleToFree = NULL;
  734. DWORD error = RemoveWorkItem(&fn, &ctx, &hMouduleToFree, &flags, m_workerIdleTimeout);
  735. ASSERT(error != ERROR_SUCCESS || !(flags & TPS_INVALID_FLAGS));
  736. if (g_bTpsTerminating)
  737. {
  738. break;
  739. }
  740. if (error == ERROR_SUCCESS)
  741. {
  742. // call the work function
  743. ((LPTHREAD_START_ROUTINE)fn)((LPVOID)ctx);
  744. if (hMouduleToFree)
  745. {
  746. // we completed the task, so free the hmodule associated with it
  747. FreeLibrary(hMouduleToFree);
  748. }
  749. if (flags & TPS_LONGEXECTIME)
  750. {
  751. MakeAvailable();
  752. }
  753. }
  754. else if (error == WAIT_TIMEOUT)
  755. {
  756. m_qlock.Acquire();
  757. if ((m_queueSize == 0) && (m_availableWorkerThreads > m_minWorkerThreads))
  758. {
  759. RemoveWorker();
  760. m_qlock.Release();
  761. //#if DBG
  762. //char buf[256];
  763. //wsprintf(buf, ">>>> terminating worker thread. Total = %d/%d. Avail = %d. Factor = %d/%d\n",
  764. // m_totalWorkerThreads,
  765. // m_maxWorkerThreadsCreated,
  766. // m_availableWorkerThreads,
  767. // m_qFactor,
  768. // m_qFactorMax
  769. // );
  770. //OutputDebugString(buf);
  771. //#endif
  772. return;
  773. }
  774. m_qlock.Release();
  775. }
  776. }
  777. //#if DBG
  778. //char buf[256];
  779. //wsprintf(buf, ">>>> terminating worker thread. Total = %d/%d. Avail = %d. Factor = %d/%d\n",
  780. // m_totalWorkerThreads,
  781. // m_maxWorkerThreadsCreated,
  782. // m_availableWorkerThreads,
  783. // m_qFactor,
  784. // m_qFactorMax
  785. // );
  786. //OutputDebugString(buf);
  787. //#endif
  788. RemoveWorker();
  789. }