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.

1108 lines
26 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. thread_pool.cxx
  5. Abstract:
  6. THREAD_POOL implementation
  7. THREAD_POOL_DATA definition and implementation
  8. Author:
  9. Taylor Weiss (TaylorW) 12-Jan-2000
  10. Jeffrey Wall (jeffwall) April 2001
  11. --*/
  12. #include <iis.h>
  13. #include <dbgutil.h>
  14. #include <thread_pool.h>
  15. #include "thread_pool_private.h"
  16. #include "thread_manager.h"
  17. #include <reftrace.h>
  18. /**********************************************************************
  19. Globals
  20. **********************************************************************/
  21. static
  22. CONST TCHAR s_szConfigRegKey[] =
  23. TEXT("System\\CurrentControlSet\\Services\\InetInfo\\Parameters");
  24. //static
  25. BOOL
  26. THREAD_POOL::CreateThreadPool(THREAD_POOL ** ppThreadPool,
  27. THREAD_POOL_CONFIG * pThreadPoolConfig)
  28. /*++
  29. Routine Description:
  30. Creates and initializes a THREAD_POOL object
  31. Arguments:
  32. ppThreadPool - storage for pointer of allocated THREAD_POOL
  33. Return Value:
  34. BOOL - TRUE if pool successfully created and initialized, else FALSE
  35. --*/
  36. {
  37. BOOL fRet = FALSE;
  38. THREAD_POOL * pThreadPool = NULL;
  39. THREAD_POOL_DATA * pData = NULL;
  40. DBG_ASSERT(NULL != ppThreadPool);
  41. *ppThreadPool = NULL;
  42. pThreadPool = new THREAD_POOL;
  43. if (NULL == pThreadPool)
  44. {
  45. fRet = FALSE;
  46. goto done;
  47. }
  48. pData = new THREAD_POOL_DATA(pThreadPool);
  49. if (NULL == pData)
  50. {
  51. fRet = FALSE;
  52. goto done;
  53. }
  54. // give threadpool object ownership of THREAD_POOL_DATA memory
  55. pThreadPool->m_pData = pData;
  56. pData = NULL;
  57. fRet = pThreadPool->m_pData->InitializeThreadPool(pThreadPoolConfig);
  58. if (FALSE == fRet)
  59. {
  60. goto done;
  61. }
  62. // created and initialized thread pool returned
  63. *ppThreadPool = pThreadPool;
  64. pThreadPool = NULL;
  65. fRet = TRUE;
  66. done:
  67. if (pThreadPool)
  68. {
  69. pThreadPool->TerminateThreadPool();
  70. pThreadPool = NULL;
  71. }
  72. return fRet;
  73. }
  74. THREAD_POOL::THREAD_POOL()
  75. /*++
  76. Routine Description:
  77. THREAD_POOL constructor
  78. Interesting work occurs in InitializeThreadPool
  79. Arguments:
  80. none
  81. Return Value:
  82. none
  83. --*/
  84. {
  85. m_pData = NULL;
  86. }
  87. THREAD_POOL::~THREAD_POOL()
  88. /*++
  89. Routine Description:
  90. THREAD_POOL destructor
  91. Interesting work occurs in TerminateThreadPool
  92. Arguments:
  93. none
  94. Return Value:
  95. none
  96. --*/
  97. {
  98. delete m_pData;
  99. m_pData = NULL;
  100. }
  101. HRESULT
  102. InitializeThreadPoolConfigWithDefaults(THREAD_POOL_CONFIG * pThreadPoolConfig)
  103. {
  104. /*++
  105. Routine Description:
  106. Calculate/Set default values for the THREAD_POOL_CONFIG
  107. Arguments:
  108. pThreadPoolConfig - structure of config parametes
  109. Return Value:
  110. HRESULT
  111. --*/
  112. DBG_ASSERT(NULL != pThreadPoolConfig);
  113. ZeroMemory(pThreadPoolConfig, sizeof(THREAD_POOL_CONFIG));
  114. BOOL IsNtServer;
  115. INITIALIZE_PLATFORM_TYPE();
  116. //
  117. // Only scale for NT Server
  118. //
  119. IsNtServer = TsIsNtServer();
  120. SYSTEM_INFO si;
  121. GetSystemInfo( &si );
  122. g_dwcCPU = si.dwNumberOfProcessors;
  123. if( IsNtServer )
  124. {
  125. MEMORYSTATUS ms;
  126. //
  127. // get the memory size
  128. //
  129. ms.dwLength = sizeof(MEMORYSTATUS);
  130. GlobalMemoryStatus( &ms );
  131. //
  132. // Alloc two threads per MB of memory.
  133. //
  134. pThreadPoolConfig->dwAbsoluteMaximumThreadCount = (LONG)((ms.dwTotalPhys >> 19) + 2);
  135. if ( pThreadPoolConfig->dwAbsoluteMaximumThreadCount < THREAD_POOL_REG_MIN_POOL_THREAD_LIMIT )
  136. {
  137. pThreadPoolConfig->dwAbsoluteMaximumThreadCount = THREAD_POOL_REG_MIN_POOL_THREAD_LIMIT;
  138. }
  139. else if ( pThreadPoolConfig->dwAbsoluteMaximumThreadCount > THREAD_POOL_REG_MAX_POOL_THREAD_LIMIT )
  140. {
  141. pThreadPoolConfig->dwAbsoluteMaximumThreadCount = THREAD_POOL_REG_MAX_POOL_THREAD_LIMIT;
  142. }
  143. }
  144. else
  145. {
  146. // Not server
  147. pThreadPoolConfig->dwAbsoluteMaximumThreadCount = THREAD_POOL_REG_MIN_POOL_THREAD_LIMIT;
  148. }
  149. // the Concurrency factor for the completion port
  150. //
  151. pThreadPoolConfig->dwConcurrency
  152. = THREAD_POOL_REG_DEF_PER_PROCESSOR_CONCURRENCY;
  153. //
  154. // the count of threads to be allowed per processor
  155. //
  156. pThreadPoolConfig->dwSoftLimitThreadCount
  157. = THREAD_POOL_REG_DEF_PER_PROCESSOR_THREADS * g_dwcCPU;
  158. //
  159. // the time (in seconds) of how long the threads
  160. // can stay alive when there is no IO operation happening on
  161. // that thread.
  162. //
  163. pThreadPoolConfig->dwThreadTimeout
  164. = THREAD_POOL_REG_DEF_THREAD_TIMEOUT * 1000;
  165. //
  166. // Read the number of threads to start
  167. // with a default of one per CPU and a floor of 4
  168. //
  169. if (g_dwcCPU < 4)
  170. {
  171. pThreadPoolConfig->dwInitialThreadCount = 4;
  172. }
  173. else
  174. {
  175. pThreadPoolConfig->dwInitialThreadCount = g_dwcCPU;
  176. }
  177. pThreadPoolConfig->dwMaxCPUUsage
  178. = THREAD_POOL_MAX_CPU_USAGE_DEFAULT;
  179. pThreadPoolConfig->dwPerSecondContextSwitchMax
  180. = THREAD_POOL_CONTEXT_SWITCH_RATE;
  181. pThreadPoolConfig->dwTimerPeriod
  182. = THREAD_POOL_TIMER_CALLBACK;
  183. pThreadPoolConfig->dwExactThreadCount
  184. = THREAD_POOL_EXACT_NUMBER_OF_THREADS_DEFAULT;
  185. return S_OK;
  186. }
  187. HRESULT
  188. OverrideThreadPoolConfigWithRegistry(
  189. IN OUT THREAD_POOL_CONFIG * pThreadPoolConfig,
  190. IN WCHAR * pszRegistryPath )
  191. {
  192. /*++
  193. Routine Description:
  194. Override the default threadpool values
  195. (as set by InitializeThreadPoolConfigWithDefaults) with values stored in registry
  196. Arguments:
  197. pThreadPoolConfig - structure of config parametes
  198. pszRegistryPath - location in the registry where the overriding parameters are stored
  199. Return Value:
  200. HRESULT
  201. --*/
  202. DBG_ASSERT(NULL != pThreadPoolConfig);
  203. //
  204. // Get configuration parameters from the registry
  205. //
  206. HKEY hKey = NULL;
  207. DWORD dwVal;
  208. DWORD dwError;
  209. //
  210. // BUGBUG - ACL may deny this if process level is insufficient
  211. //
  212. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  213. pszRegistryPath,
  214. 0,
  215. KEY_READ,
  216. &hKey
  217. );
  218. if( dwError == NO_ERROR )
  219. {
  220. //
  221. // Read the Concurrency factor for the completion port
  222. //
  223. pThreadPoolConfig->dwConcurrency
  224. = I_ThreadPoolReadRegDword(
  225. hKey,
  226. THREAD_POOL_REG_PER_PROCESSOR_CONCURRENCY,
  227. pThreadPoolConfig->dwConcurrency
  228. );
  229. //
  230. // Read the count of threads to be allowed per processor
  231. //
  232. pThreadPoolConfig->dwSoftLimitThreadCount
  233. = I_ThreadPoolReadRegDword(
  234. hKey,
  235. THREAD_POOL_REG_PER_PROCESSOR_THREADS,
  236. pThreadPoolConfig->dwSoftLimitThreadCount
  237. );
  238. //
  239. // Read the time (in seconds) of how long the threads
  240. // can stay alive when there is no IO operation happening on
  241. // that thread.
  242. //
  243. pThreadPoolConfig->dwThreadTimeout
  244. = I_ThreadPoolReadRegDword(
  245. hKey,
  246. THREAD_POOL_REG_THREAD_TIMEOUT,
  247. pThreadPoolConfig->dwThreadTimeout
  248. );
  249. //
  250. // Read the max thread limit. We've already computed a limit
  251. // based on memory, but allow registry override.
  252. //
  253. pThreadPoolConfig->dwAbsoluteMaximumThreadCount
  254. = I_ThreadPoolReadRegDword(
  255. hKey,
  256. THREAD_POOL_REG_POOL_THREAD_LIMIT,
  257. pThreadPoolConfig->dwAbsoluteMaximumThreadCount
  258. );
  259. //
  260. // Read the number of threads to start
  261. // with a default of one per CPU and a floor of 4
  262. //
  263. pThreadPoolConfig->dwInitialThreadCount
  264. = I_ThreadPoolReadRegDword(
  265. hKey,
  266. THREAD_POOL_REG_POOL_THREAD_START,
  267. pThreadPoolConfig->dwInitialThreadCount
  268. );
  269. pThreadPoolConfig->dwMaxCPUUsage
  270. = I_ThreadPoolReadRegDword(
  271. hKey,
  272. THREAD_POOL_REG_MAX_CPU,
  273. pThreadPoolConfig->dwMaxCPUUsage
  274. );
  275. pThreadPoolConfig->dwPerSecondContextSwitchMax =
  276. I_ThreadPoolReadRegDword(hKey,
  277. THREAD_POOL_REG_MAX_CONTEXT_SWITCH,
  278. pThreadPoolConfig->dwPerSecondContextSwitchMax
  279. );
  280. pThreadPoolConfig->dwTimerPeriod =
  281. I_ThreadPoolReadRegDword(hKey,
  282. THREAD_POOL_REG_START_DELAY,
  283. pThreadPoolConfig->dwTimerPeriod
  284. );
  285. pThreadPoolConfig->dwExactThreadCount =
  286. I_ThreadPoolReadRegDword(hKey,
  287. THREAD_POOL_REG_EXACT_THREAD_COUNT,
  288. pThreadPoolConfig->dwExactThreadCount
  289. );
  290. RegCloseKey( hKey );
  291. hKey = NULL;
  292. }
  293. return S_OK;
  294. }
  295. BOOL
  296. THREAD_POOL_DATA::InitializeThreadPool(THREAD_POOL_CONFIG * pThreadPoolConfig)
  297. /*++
  298. Routine Description:
  299. Initializes a THREAD_POOL object.
  300. Determines thread limits, reads settings from registry
  301. creates completion port, creates THREAD_MANAGER
  302. and creates initial threads
  303. Arguments:
  304. none
  305. Return Value:
  306. BOOL - TRUE if pool successfully initialized, else FALSE
  307. --*/
  308. {
  309. BOOL fRet = FALSE;
  310. HRESULT hr = S_OK;
  311. DBG_ASSERT(NULL != pThreadPoolConfig);
  312. #if DBG
  313. HKEY hKey = NULL;
  314. DWORD dwVal;
  315. DWORD dwError;
  316. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  317. s_szConfigRegKey,
  318. 0,
  319. KEY_READ,
  320. &hKey
  321. );
  322. if( dwError == NO_ERROR )
  323. {
  324. //
  325. // Read the Reg setting for Ref Tracing
  326. //
  327. m_dwTraceRegSetting = TRACE_WHEN_NULL;
  328. m_dwTraceRegSetting = I_ThreadPoolReadRegDword(
  329. hKey,
  330. THREAD_POOL_REG_REF_TRACE_COUNTER,
  331. m_dwTraceRegSetting
  332. );
  333. RegCloseKey( hKey );
  334. hKey = NULL;
  335. }
  336. #endif
  337. #ifdef DBG
  338. // initialize refernce logging variable, else it's already set to null in the constructor
  339. if (m_dwTraceRegSetting != TRACE_NONE)
  340. {
  341. m_pTraceLog = CreateRefTraceLog(2000, 0);
  342. if( !m_pTraceLog )
  343. {
  344. fRet = FALSE;
  345. goto cleanup;
  346. }
  347. }
  348. #endif
  349. CopyMemory(&m_poolConfig, pThreadPoolConfig, sizeof(m_poolConfig));
  350. hr = THREAD_MANAGER::CreateThreadManager(&m_pThreadManager, m_pPool, this);
  351. if (FAILED(hr))
  352. {
  353. fRet = FALSE;
  354. goto cleanup;
  355. }
  356. //
  357. // Create the completion port
  358. //
  359. m_hCompPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE,
  360. NULL,
  361. 0,
  362. m_poolConfig.dwConcurrency
  363. );
  364. if( !m_hCompPort )
  365. {
  366. fRet = FALSE;
  367. goto cleanup;
  368. }
  369. // When the exact thread count is set, the initial count does not matter
  370. if ( m_poolConfig.dwExactThreadCount )
  371. {
  372. m_poolConfig.dwInitialThreadCount = m_poolConfig.dwExactThreadCount;
  373. }
  374. //
  375. // Create our initial threads
  376. //
  377. if (m_poolConfig.dwInitialThreadCount < 1)
  378. {
  379. m_poolConfig.dwInitialThreadCount = 1;
  380. }
  381. for(DWORD i = 0; i < m_poolConfig.dwInitialThreadCount; i++)
  382. {
  383. DBG_REQUIRE( m_pThreadManager->CreateThread(ThreadPoolThread,
  384. (LPVOID) this) );
  385. }
  386. fRet = TRUE;
  387. return fRet;
  388. //
  389. // Only on failure
  390. //
  391. cleanup:
  392. if( m_hCompPort != NULL )
  393. {
  394. CloseHandle( m_hCompPort );
  395. m_hCompPort = NULL;
  396. }
  397. return fRet;
  398. }
  399. VOID
  400. THREAD_POOL::TerminateThreadPool()
  401. /*++
  402. Routine Description:
  403. cleans up and destroys a THREAD_POOL object
  404. CAVEAT: blocks until all threads in pool have terminated
  405. Arguments:
  406. none
  407. Return Value:
  408. none
  409. --*/
  410. {
  411. DBGPRINTF(( DBG_CONTEXT,
  412. "W3TP: Cleaning up thread pool.\n" ));
  413. if ( m_pData->m_fShutdown )
  414. {
  415. //
  416. // We have not been intialized or have already terminated.
  417. //
  418. DBG_ASSERT( FALSE );
  419. return;
  420. }
  421. m_pData->m_fShutdown = TRUE;
  422. if ( m_pData->m_pThreadManager )
  423. {
  424. m_pData->m_pThreadManager->TerminateThreadManager(THREAD_POOL_DATA::ThreadPoolStop,
  425. m_pData);
  426. m_pData->m_pThreadManager = NULL;
  427. }
  428. if ( m_pData->m_hCompPort )
  429. {
  430. CloseHandle( m_pData->m_hCompPort );
  431. m_pData->m_hCompPort = NULL;
  432. }
  433. #if DBG
  434. // delete the logging object
  435. if ( m_pData->m_pTraceLog )
  436. {
  437. DestroyRefTraceLog(m_pData->m_pTraceLog);
  438. m_pData->m_pTraceLog = NULL;
  439. }
  440. m_pData->m_dwTraceRegSetting = 0;
  441. #endif
  442. // finally, release this objects memory
  443. delete this;
  444. return;
  445. }
  446. //static
  447. void
  448. WINAPI
  449. THREAD_POOL_DATA::ThreadPoolStop(VOID * pvThis)
  450. /*++
  451. Routine Description:
  452. posts completion to signal one thread to terminate
  453. Arguments:
  454. pvThis - THREAD_POOL this pointer
  455. Return Value:
  456. none
  457. --*/
  458. {
  459. BOOL fRes;
  460. OVERLAPPED Overlapped;
  461. ZeroMemory( &Overlapped, sizeof(OVERLAPPED) );
  462. THREAD_POOL_DATA * pThis= reinterpret_cast<THREAD_POOL_DATA*>(pvThis);
  463. fRes = PostQueuedCompletionStatus( pThis->m_hCompPort,
  464. 0,
  465. THREAD_POOL_THREAD_EXIT_KEY,
  466. &Overlapped
  467. );
  468. DBG_ASSERT( fRes ||
  469. (!fRes && GetLastError() == ERROR_IO_PENDING)
  470. );
  471. return;
  472. }
  473. ULONG_PTR
  474. THREAD_POOL::SetInfo(IN THREAD_POOL_INFO InfoId,
  475. IN ULONG_PTR Data)
  476. /*++
  477. Routine Description:
  478. Sets thread pool configuration data
  479. Arguments:
  480. InfoId - Data item to set
  481. Data - New value for item
  482. Return Value:
  483. The old data value
  484. --*/
  485. {
  486. ULONG_PTR oldVal = 0;
  487. switch ( InfoId )
  488. {
  489. //
  490. // Increment or decrement the max thread count. In this instance, we
  491. // do not scale by the number of CPUs
  492. //
  493. case ThreadPoolIncMaxPoolThreads:
  494. InterlockedIncrement( (LONG *) &m_pData->m_poolConfig.dwSoftLimitThreadCount );
  495. oldVal = TRUE;
  496. break;
  497. case ThreadPoolDecMaxPoolThreads:
  498. InterlockedDecrement( (LONG *) &m_pData->m_poolConfig.dwSoftLimitThreadCount );
  499. oldVal = TRUE;
  500. break;
  501. default:
  502. DBG_ASSERT( FALSE );
  503. break;
  504. } // switch
  505. return oldVal;
  506. } // ThreadPoolSetInfo()
  507. BOOL
  508. THREAD_POOL::BindIoCompletionCallback(HANDLE FileHandle, // handle to file
  509. LPOVERLAPPED_COMPLETION_ROUTINE Function, // callback
  510. ULONG Flags // reserved
  511. )
  512. /*++
  513. Routine Description:
  514. Binds given handle to completion port
  515. Arguments:
  516. FileHandle - handle to bind
  517. Function - function to call on completion
  518. Flags - not used
  519. Return Value:
  520. TRUE if handle bound to port, otherwise FALSE
  521. --*/
  522. {
  523. DBG_ASSERT( FileHandle && FileHandle != INVALID_HANDLE_VALUE );
  524. DBG_ASSERT( Function );
  525. DBG_ASSERT( m_pData->m_hCompPort );
  526. return ( CreateIoCompletionPort( FileHandle,
  527. m_pData->m_hCompPort,
  528. (ULONG_PTR)Function,
  529. m_pData->m_poolConfig.dwConcurrency ) != NULL );
  530. }
  531. BOOL
  532. THREAD_POOL::PostCompletion(IN DWORD dwBytesTransferred,
  533. IN LPOVERLAPPED_COMPLETION_ROUTINE function,
  534. IN LPOVERLAPPED lpo)
  535. /*++
  536. Routine Description:
  537. Posts a completion to the port. Results in an asynchronous callback.
  538. Arguments:
  539. dwBytesTransferred - bytes transferred for this completions
  540. Function - function to call on completion
  541. lpo - overlapped pointer
  542. Return Value:
  543. TRUE if completion posted, otherwise FALSE
  544. --*/
  545. {
  546. DBG_ASSERT( m_pData->m_hCompPort && m_pData->m_hCompPort != INVALID_HANDLE_VALUE );
  547. #if DBG
  548. //
  549. // If m_pTraceData is not null then
  550. // Trace the function pointer when lpo is zero and reg setting is TRACE_WHEN NULL (1)
  551. // or when reg setting is TRACE_ALWAYS (2)
  552. //
  553. if ( m_pData->m_pTraceLog )
  554. {
  555. if ( ( !lpo && (m_pData->m_dwTraceRegSetting == TRACE_WHEN_NULL) ) ||
  556. ( m_pData->m_dwTraceRegSetting == TRACE_ALWAYS )
  557. )
  558. {
  559. WriteRefTraceLog(m_pData->m_pTraceLog, 0, function );
  560. }
  561. }
  562. #endif
  563. return ( PostQueuedCompletionStatus( m_pData->m_hCompPort,
  564. dwBytesTransferred,
  565. (ULONG_PTR)function,
  566. lpo ) != NULL );
  567. }
  568. BOOL
  569. ThreadHasIOPending()
  570. /*++
  571. Routine Description:
  572. Determine if the current threads has any outstanding I/O associated with it
  573. Arguments:
  574. VOID
  575. Return Value:
  576. BOOL - TRUE indicates I/O is pending on this thread
  577. --*/
  578. {
  579. //
  580. // BUGBUG - Dependency on ntdll
  581. //
  582. NTSTATUS NtStatus;
  583. ULONG ThreadHasPendingIo = TRUE;
  584. NtStatus = NtQueryInformationThread( NtCurrentThread(),
  585. ThreadIsIoPending,
  586. &ThreadHasPendingIo,
  587. sizeof(ThreadHasPendingIo),
  588. NULL);
  589. DBG_ASSERT( NT_SUCCESS( NtStatus ) );
  590. return !!ThreadHasPendingIo;
  591. }
  592. //
  593. // Thread pool thread function
  594. //
  595. //static
  596. DWORD
  597. THREAD_POOL_DATA::ThreadPoolThread(
  598. LPVOID pvThis
  599. )
  600. /*++
  601. Routine Description:
  602. Thread pool thread function
  603. Arguments:
  604. pvThis - pointer to THREAD_POOL
  605. Return Value:
  606. Thread return value (ignored)
  607. --*/
  608. {
  609. THREAD_POOL_DATA *pThis = reinterpret_cast<THREAD_POOL_DATA*>(pvThis);
  610. DBG_ASSERT(pThis);
  611. BOOL fFirst = FALSE;
  612. DWORD dwRet = ERROR_SUCCESS;
  613. //
  614. // Increment the total thread count and mark the
  615. // threads that we spin up at startup to not timeout
  616. //
  617. if ( pThis->m_poolConfig.dwInitialThreadCount >=
  618. (DWORD) InterlockedIncrement( &pThis->m_cThreads ) )
  619. {
  620. fFirst = TRUE;
  621. }
  622. for (;;)
  623. {
  624. //
  625. // Begin looping on I/O completion port
  626. //
  627. dwRet = pThis->ThreadPoolThread();
  628. //
  629. // always exit threads at shutdown
  630. //
  631. if ( pThis->m_fShutdown )
  632. {
  633. break;
  634. }
  635. //
  636. // Keep the initial threads alive.
  637. //
  638. if( TRUE == fFirst )
  639. {
  640. continue;
  641. }
  642. //
  643. // Threads cannot exit if I/O is pending on it
  644. //
  645. if ( ThreadHasIOPending() )
  646. {
  647. continue;
  648. }
  649. //
  650. // thread returned from completion processing function
  651. // and has no reason to stick around
  652. //
  653. break;
  654. } // for(;;)
  655. //
  656. // Let ThreadPoolTerminate know that all the threads are dead
  657. //
  658. InterlockedDecrement( &pThis->m_cThreads );
  659. // Unless shutting down, threads must not exit with pending I/O
  660. DBG_ASSERT( pThis->m_fShutdown || !ThreadHasIOPending());
  661. // Unless shutting down, the threads created first must not exit
  662. DBG_ASSERT( pThis->m_fShutdown || !fFirst );
  663. return dwRet;
  664. }
  665. DWORD
  666. THREAD_POOL_DATA::ThreadPoolThread()
  667. /*++
  668. Routine Description:
  669. Thread pool thread function
  670. Arguments:
  671. none
  672. Return Value:
  673. Thread return value (ignored)
  674. --*/
  675. {
  676. BOOL fRet;
  677. DWORD BytesTransfered;
  678. LPOVERLAPPED lpo = NULL;
  679. DWORD ReturnCode = ERROR_SUCCESS;
  680. DWORD LastError;
  681. LPOVERLAPPED_COMPLETION_ROUTINE CompletionCallback;
  682. for(;;)
  683. {
  684. lpo = NULL;
  685. //
  686. // wait for the configured timeout
  687. //
  688. InterlockedIncrement( &m_cAvailableThreads );
  689. fRet = GetQueuedCompletionStatus( m_hCompPort, // completion port to wait on
  690. &BytesTransfered, // number of bytes transferred
  691. (ULONG_PTR *)&CompletionCallback, // function pointer
  692. &lpo, // buffer to fill
  693. m_poolConfig.dwThreadTimeout // timeout in milliseconds
  694. );
  695. InterlockedDecrement( &m_cAvailableThreads );
  696. LastError = fRet ? ERROR_SUCCESS : GetLastError();
  697. if( fRet || lpo )
  698. {
  699. //
  700. // There was a completion.
  701. //
  702. if( CompletionCallback ==
  703. (LPOVERLAPPED_COMPLETION_ROUTINE) THREAD_POOL_THREAD_EXIT_KEY )
  704. {
  705. //
  706. // signal to exit this thread
  707. //
  708. ReturnCode = ERROR_SUCCESS;
  709. break;
  710. }
  711. DBG_ASSERT ( CompletionCallback );
  712. //
  713. // This thread is about to go do work so check the state of the pool
  714. //
  715. ThreadPoolCheckThreadStatus();
  716. //
  717. // Call the completion function.
  718. //
  719. CompletionCallback( LastError, BytesTransfered, lpo );
  720. }
  721. else
  722. {
  723. //
  724. // No completion, timeout or error.
  725. // Something bad happened or thread timed out.
  726. //
  727. ReturnCode = LastError;
  728. break;
  729. }
  730. } // for(;;)
  731. return ReturnCode;
  732. }
  733. BOOL WINAPI
  734. THREAD_POOL_DATA::OkToCreateAnotherThread()
  735. /*++
  736. Routine Description:
  737. determines whether or not thread pool should have another thread created
  738. based on shutting down, exact thread count, available threads, current limit, and max limit
  739. Arguments:
  740. void
  741. Return Value:
  742. TRUE if another thread is ok to create, otherwise FALSE
  743. --*/
  744. {
  745. if (!m_fShutdown &&
  746. (m_poolConfig.dwExactThreadCount == 0) &&
  747. (m_cAvailableThreads == 0) &&
  748. ((DWORD)m_cThreads < m_poolConfig.dwSoftLimitThreadCount) &&
  749. ((DWORD)m_cThreads < m_poolConfig.dwAbsoluteMaximumThreadCount) )
  750. {
  751. return TRUE;
  752. }
  753. return FALSE;
  754. }
  755. BOOL
  756. THREAD_POOL_DATA::ThreadPoolCheckThreadStatus()
  757. /*++
  758. Routine Description:
  759. Make sure there is at least one thread in the thread pool.
  760. We're fast and loose so a couple of extra threads may be
  761. created.
  762. Arguments:
  763. ThreadParam - usually NULL, may be used to signify
  764. special thread status.
  765. Return Value:
  766. TRUE if successful
  767. FALSE thread
  768. --*/
  769. {
  770. BOOL fRet = TRUE;
  771. // CODEWORK: Should investigate making this stickier. It should
  772. // not be quite so easy to create threads.
  773. if ( OkToCreateAnotherThread() )
  774. {
  775. DBG_ASSERT( NULL != m_pThreadManager );
  776. m_pThreadManager->RequestThread(ThreadPoolThread, // thread function
  777. this // thread argument
  778. );
  779. }
  780. return fRet;
  781. }
  782. /**********************************************************************
  783. Private function definitions
  784. **********************************************************************/
  785. DWORD
  786. I_ThreadPoolReadRegDword(
  787. IN HKEY hKey,
  788. IN LPCTSTR pszValueName,
  789. IN DWORD dwDefaultValue
  790. )
  791. /*++
  792. Routine Description:
  793. Reads a DWORD value from the registry
  794. Arguments:
  795. hKey - Opened registry key to read
  796. pszValueName - The name of the value.
  797. dwDefaultValue - The default value to use if the
  798. value cannot be read.
  799. Return Value:
  800. DWORD - The value from the registry, or dwDefaultValue.
  801. --*/
  802. {
  803. DWORD err;
  804. DWORD dwBuffer;
  805. DWORD cbBuffer = sizeof(dwBuffer);
  806. DWORD dwType;
  807. if( hKey != NULL )
  808. {
  809. err = RegQueryValueEx( hKey,
  810. pszValueName,
  811. NULL,
  812. &dwType,
  813. (LPBYTE)&dwBuffer,
  814. &cbBuffer
  815. );
  816. if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) )
  817. {
  818. dwDefaultValue = dwBuffer;
  819. }
  820. }
  821. return dwDefaultValue;
  822. }