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.

1184 lines
26 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name:
  4. thrpool.cpp
  5. Abstract:
  6. Contains the Win32 Thread Pooling Class, ThreadPool
  7. There may be a handle leak in this module.
  8. Author:
  9. Tad Brockway (tadb) 9/99
  10. Revision History:
  11. --*/
  12. #include <precom.h>
  13. #define TRC_FILE "thrpool"
  14. #include "drobject.h"
  15. #include "rdpll.h"
  16. #include "thrpool.h"
  17. #if DBG
  18. #include <stdlib.h>
  19. #endif
  20. ///////////////////////////////////////////////////////////////
  21. //
  22. // ThreadPool Members
  23. //
  24. ThreadPool::ThreadPool(
  25. OPTIONAL IN ULONG minThreads,
  26. OPTIONAL IN ULONG maxThreads,
  27. OPTIONAL IN DWORD threadExitTimeout
  28. )
  29. /*++
  30. Routine Description:
  31. Constructor
  32. Arguments:
  33. minThreads - Once this number of threads has been created,
  34. the number of available threads will not be
  35. reduced below this value.
  36. maxThreads - Number of simultaneous threads in the
  37. pool should not exceed this value.
  38. threadExitTimeout - Number of ms to block while waiting for a
  39. thread in the pool to exit. Should be set
  40. to INFINITE if we should block indefinitely
  41. waiting on the thread to exit.
  42. Return Value:
  43. NA
  44. --*/
  45. {
  46. DC_BEGIN_FN("ThreadPool::ThreadPool");
  47. //
  48. // Not valid until initialized.
  49. //
  50. SetValid(FALSE);
  51. _initialized = FALSE;
  52. //
  53. // Sanity check params.
  54. //
  55. if (maxThreads < minThreads) {
  56. TRC_ERR(
  57. (TB, _T("Max threads value %ld smaller than min threads value %ld."),
  58. maxThreads, minThreads));
  59. ASSERT(FALSE);
  60. _maxThreads = 0;
  61. _minThreads = 0;
  62. }
  63. else {
  64. _maxThreads = maxThreads;
  65. _minThreads = minThreads;
  66. }
  67. _threadExitTimeout = threadExitTimeout;
  68. //
  69. // Zero the lock and thread counts.
  70. //
  71. #if DBG
  72. _lockCount = 0;
  73. #endif
  74. _threadCount = 0;
  75. //
  76. // Initialize the thread list pointers.
  77. //
  78. InitializeListHead(&_threadListHead);
  79. DC_END_FN();
  80. }
  81. ThreadPool::~ThreadPool()
  82. /*++
  83. Routine Description:
  84. Destructor
  85. Arguments:
  86. Return Value:
  87. NA
  88. --*/
  89. {
  90. DC_BEGIN_FN("ThreadPool::~ThreadPool");
  91. //
  92. // Clean up the critical section.
  93. //
  94. if (_initialized) {
  95. //
  96. // Remove all pending threads.
  97. //
  98. RemoveAllThreads();
  99. DeleteCriticalSection(&_cs);
  100. }
  101. DC_END_FN();
  102. }
  103. VOID
  104. ThreadPool::RemoveAllThreads()
  105. /*++
  106. Routine Description:
  107. Remove all the outstanding threads
  108. Arguments:
  109. Return Value:
  110. NA
  111. --*/
  112. {
  113. PTHREADPOOL_THREAD thr;
  114. PLIST_ENTRY listEntry;
  115. DC_BEGIN_FN("ThreadPool::RemoveAllThreads");
  116. //
  117. // Remove all pending threads.
  118. //
  119. Lock();
  120. listEntry = _threadListHead.Flink;
  121. while (listEntry != &_threadListHead) {
  122. thr = CONTAINING_RECORD(listEntry, THREADPOOL_THREAD, _listEntry);
  123. if ((thr->_listEntry.Blink != NULL) &&
  124. (thr->_listEntry.Flink != NULL)) {
  125. RemoveEntryList(&thr->_listEntry);
  126. thr->_listEntry.Flink = NULL;
  127. thr->_listEntry.Blink = NULL;
  128. }
  129. _threadCount--;
  130. Unlock();
  131. CleanUpThread(thr, _threadExitTimeout);
  132. Lock();
  133. listEntry = _threadListHead.Flink;
  134. }
  135. Unlock();
  136. DC_END_FN();
  137. }
  138. DWORD
  139. ThreadPool::Initialize()
  140. /*++
  141. Routine Description:
  142. Initialize an instance of this class.
  143. Arguments:
  144. Return Value:
  145. ERROR_SUCCEESS on success. Otherwise, an error status is returned.
  146. --*/
  147. {
  148. DWORD result = ERROR_SUCCESS;
  149. DC_BEGIN_FN("ThreadPool::Initialize");
  150. //
  151. // Initialize the critical section.
  152. //
  153. __try {
  154. InitializeCriticalSection(&_cs);
  155. _initialized = TRUE;
  156. SetValid(TRUE);
  157. }
  158. __except(EXCEPTION_EXECUTE_HANDLER) {
  159. result = GetExceptionCode();
  160. }
  161. DC_END_FN();
  162. return result;
  163. }
  164. ThreadPoolRequest
  165. ThreadPool::SubmitRequest(
  166. IN ThreadPoolFunc func,
  167. OPTIONAL IN PVOID clientData,
  168. OPTIONAL IN HANDLE completionEvent
  169. )
  170. /*++
  171. Routine Description:
  172. Submit an asynchronous request to a thread in the pool.
  173. Arguments:
  174. func - Request function to execute.
  175. clientData - Associated client data.
  176. completionEvent - Optional completion event that will
  177. be signalled when the operation is complete.
  178. Return Value:
  179. A handle to the request. Returns INVALID_THREADPOOLREQUEST on
  180. error.
  181. --*/
  182. {
  183. DC_BEGIN_FN("ThreadPool::SubmitRequest");
  184. PLIST_ENTRY listEntry;
  185. PTHREADPOOL_THREAD thr, srchThr;
  186. SmartPtr<ThreadPoolReq > *ptr = NULL;
  187. Lock();
  188. //
  189. // Search for an unused thread.
  190. //
  191. thr = NULL;
  192. listEntry = _threadListHead.Flink;
  193. while (listEntry != &_threadListHead) {
  194. srchThr = CONTAINING_RECORD(listEntry, THREADPOOL_THREAD, _listEntry);
  195. if (srchThr->_pendingRequest == NULL) {
  196. thr = srchThr;
  197. break;
  198. }
  199. listEntry = listEntry->Flink;
  200. }
  201. //
  202. // Allocate a new thread if necessary.
  203. //
  204. if (thr == NULL) {
  205. thr = AddNewThreadToPool();
  206. }
  207. //
  208. // If we got a thread, then allocate the request.
  209. //
  210. if (thr != NULL) {
  211. //
  212. // Allocate the smart pointer.
  213. //
  214. ptr = new SmartPtr<ThreadPoolReq >;
  215. if (ptr == NULL) {
  216. TRC_ERR((TB, _T("Error allocating smart pointer.")));
  217. thr = NULL;
  218. goto Cleanup;
  219. }
  220. //
  221. // Point to a new request.
  222. //
  223. (*ptr) = new ThreadPoolReq;
  224. if ((*ptr) != NULL) {
  225. (*ptr)->_func = func;
  226. (*ptr)->_clientData = clientData;
  227. (*ptr)->_completionEvent = completionEvent;
  228. (*ptr)->_completionStatus = ERROR_IO_PENDING;
  229. //
  230. // Give the thread a reference to the request.
  231. //
  232. thr->_pendingRequest = (*ptr);
  233. }
  234. else {
  235. TRC_ERR((TB, _T("Error allocating request.")));
  236. delete ptr;
  237. ptr = NULL;
  238. thr = NULL;
  239. goto Cleanup;
  240. }
  241. }
  242. else {
  243. goto Cleanup;
  244. }
  245. Cleanup:
  246. Unlock();
  247. //
  248. // Wake up the thread if we were successful.
  249. //
  250. if (thr != NULL) {
  251. SetEvent(thr->_synchronizationEvent);
  252. }
  253. DC_END_FN();
  254. //
  255. // Return the smart pointer to the request.
  256. //
  257. return ptr;
  258. }
  259. DWORD
  260. ThreadPool::GetRequestCompletionStatus(
  261. IN ThreadPoolRequest req
  262. )
  263. /*++
  264. Routine Description:
  265. Return the completion status for a request.
  266. Arguments:
  267. req - A handle the request, as returned by ThreadPool::SubmitRequest.
  268. Return Value:
  269. Pointer to request-associated client data.
  270. --*/
  271. {
  272. SmartPtr<ThreadPoolReq> *ptr;
  273. DC_BEGIN_FN("ThreadPool::GetRequestCompletionStatus");
  274. //
  275. // Request is really a smart pointer to a request.
  276. //
  277. ptr = (SmartPtr<ThreadPoolReq> *)req;
  278. ASSERT((*ptr)->IsValid());
  279. DC_END_FN();
  280. return (*ptr)->_completionStatus;
  281. }
  282. VOID
  283. ThreadPool::CloseRequest(
  284. IN ThreadPoolRequest req
  285. )
  286. /*++
  287. Routine Description:
  288. Close a request submitted by a call to SubmitRequest. This should generally
  289. be called after the request has finished. Otherwise, the completion event
  290. will not be signalled when the request ultimately finishes.
  291. Arguments:
  292. req - A handle the request, as returned by ThreadPool::QueueRequest.
  293. Return Value:
  294. Pointer to request-associated client data.
  295. --*/
  296. {
  297. SmartPtr<ThreadPoolReq> *ptr;
  298. DC_BEGIN_FN("ThreadPool::GetRequestClientData");
  299. //
  300. // Request is really a smart pointer to a request.
  301. //
  302. ptr = (SmartPtr<ThreadPoolReq> *)req;
  303. ASSERT((*ptr)->IsValid());
  304. //
  305. // Lock the request and zero its completion event.
  306. //
  307. Lock();
  308. (*ptr)->_completionEvent = NULL;
  309. Unlock();
  310. //
  311. // Dereference the request.
  312. //
  313. (*ptr) = NULL;
  314. //
  315. // Delete the smart pointer.
  316. //
  317. delete ptr;
  318. DC_END_FN();
  319. }
  320. PVOID
  321. ThreadPool::GetRequestClientData(
  322. IN ThreadPoolRequest req
  323. )
  324. /*++
  325. Routine Description:
  326. Return a pointer to the client data for a request.
  327. Arguments:
  328. req - A handle the request, as returned by ThreadPool::QueueRequest.
  329. Return Value:
  330. Pointer to request-associated client data.
  331. --*/
  332. {
  333. SmartPtr<ThreadPoolReq> *ptr;
  334. DC_BEGIN_FN("ThreadPool::GetRequestClientData");
  335. //
  336. // Request is really a smart pointer to a request.
  337. //
  338. ptr = (SmartPtr<ThreadPoolReq> *)req;
  339. ASSERT((*ptr)->IsValid());
  340. DC_END_FN();
  341. return (*ptr)->_clientData;
  342. }
  343. VOID
  344. ThreadPool::RemoveThreadFromPool(
  345. PTHREADPOOL_THREAD thread,
  346. DWORD timeout
  347. )
  348. /*++
  349. Routine Description:
  350. Remove a thread from the pool.
  351. Arguments:
  352. thread - The thread to remove from the pool
  353. timeOut - Number of MS to wait for the thread to exit before
  354. killing it. This should be set to INFINITE if this
  355. function should block indefinitely waiting for the thread
  356. to exit.
  357. Return Value:
  358. NA
  359. --*/
  360. {
  361. DC_BEGIN_FN("ThreadPool::RemoveThreadFromPool");
  362. ASSERT(thread != NULL);
  363. TRC_NRM((TB, _T("Removing thread %ld from pool."), thread->_tid));
  364. //
  365. // Make sure it's still in the list and unlink it. If it's not in
  366. // the list, then we have been reentered with the same thread.
  367. //
  368. Lock();
  369. if ((thread->_listEntry.Blink != NULL) &&
  370. (thread->_listEntry.Flink != NULL)) {
  371. RemoveEntryList(&thread->_listEntry);
  372. thread->_listEntry.Flink = NULL;
  373. thread->_listEntry.Blink = NULL;
  374. }
  375. else {
  376. TRC_ALT((TB, _T("Thread %ld being removed 2x. This is okay."),
  377. thread->_tid));
  378. Unlock();
  379. return;
  380. }
  381. _threadCount--;
  382. Unlock();
  383. //
  384. // Clean it up.
  385. //
  386. CleanUpThread(thread, timeout);
  387. }
  388. VOID
  389. ThreadPool::CleanUpThread(
  390. PTHREADPOOL_THREAD thread,
  391. DWORD timeout
  392. )
  393. /*++
  394. Routine Description:
  395. Notify a thread to shut down, wait for it to finish, and clean up.
  396. Arguments:
  397. thread - The thread to remove from the pool
  398. timeOut - Number of MS to wait for the thread to exit before
  399. killing it. This should be set to INFINITE if this
  400. function should block indefinitely waiting for the thread
  401. to exit.
  402. Return Value:
  403. NA
  404. --*/
  405. {
  406. DWORD waitResult;
  407. DC_BEGIN_FN("ThreadPool::RemoveThreadFromPool");
  408. ASSERT(thread != NULL);
  409. //
  410. // Set the exit flag and wait for the thread to finish.
  411. //
  412. TRC_NRM((TB, _T("Shutting down thread %ld"), thread->_tid));
  413. thread->_exitFlag = TRUE;
  414. SetEvent(thread->_synchronizationEvent);
  415. ASSERT(thread->_threadHandle != NULL);
  416. waitResult = WaitForSingleObject(thread->_threadHandle, timeout);
  417. if (waitResult != WAIT_OBJECT_0) {
  418. #if DBG
  419. if (waitResult == WAIT_FAILED) {
  420. TRC_ERR((TB, _T("Wait failed for tid %ld: %ld."),
  421. GetLastError(), thread->_tid));
  422. }
  423. else if (waitResult == WAIT_ABANDONED) {
  424. TRC_ERR((TB, _T("Wait abandoned for tid %ld."), thread->_tid));
  425. }
  426. else if (waitResult == WAIT_TIMEOUT) {
  427. TRC_ERR((TB, _T("Wait timed out for tid %ld."), thread->_tid));
  428. }
  429. else {
  430. TRC_ERR((TB, _T("Unknown wait return status.")));
  431. ASSERT(FALSE);
  432. }
  433. #endif
  434. TRC_ERR((TB,
  435. _T("Error waiting for background thread %ld to exit."),
  436. thread->_tid));
  437. //
  438. // If we ever hit this, then we have a production level bug that will possibly
  439. // corrupt the integrity of this process, so we will 'break' even on free
  440. // builds.
  441. //
  442. DebugBreak();
  443. }
  444. else {
  445. TRC_NRM((TB, _T("Background thread %ld shut down on its own."), thread->_tid));
  446. }
  447. //
  448. // Finish the request if one is pending.
  449. //
  450. if (thread->_pendingRequest != NULL) {
  451. //
  452. // Fire the request completion event.
  453. //
  454. Lock();
  455. if (thread->_pendingRequest->_completionEvent != NULL) {
  456. SetEvent(thread->_pendingRequest->_completionEvent);
  457. }
  458. Unlock();
  459. thread->_pendingRequest->_completionStatus = ERROR_CANCELLED;
  460. //
  461. // Dereference the request.
  462. //
  463. thread->_pendingRequest = NULL;
  464. }
  465. //
  466. // Delete the thread object.
  467. //
  468. delete thread;
  469. DC_END_FN();
  470. }
  471. ThreadPool::PTHREADPOOL_THREAD
  472. ThreadPool::AddNewThreadToPool()
  473. /*++
  474. Routine Description:
  475. Add a new thread to the pool and return it.
  476. Arguments:
  477. NA
  478. Return Value:
  479. The new thread. NULL if unable to create a new thread.
  480. --*/
  481. {
  482. PTHREADPOOL_THREAD newThread = NULL;
  483. DC_BEGIN_FN("ThreadPool::AddNewThreadToPool");
  484. Lock();
  485. //
  486. // Make sure we haven't reached the max thread count.
  487. //
  488. if (GetThreadCount() < _maxThreads) {
  489. newThread = new THREADPOOL_THREAD;
  490. if (newThread == NULL) {
  491. TRC_ERR((TB, _T("Error allocating new thread.")));
  492. }
  493. }
  494. else {
  495. TRC_ERR((TB, _T("Max thread count %ld reached."), _maxThreads));
  496. }
  497. //
  498. // Create the synchronization event.
  499. //
  500. if (newThread != NULL) {
  501. newThread->_synchronizationEvent =
  502. CreateEvent(
  503. NULL, // no attribute.
  504. FALSE, // auto reset.
  505. FALSE, // initially not signalled.
  506. NULL // no name.
  507. );
  508. if (newThread->_synchronizationEvent == NULL) {
  509. TRC_ERR((TB, _T("Can't create event for new thread: %08X."),
  510. GetLastError()));
  511. delete newThread;
  512. newThread = NULL;
  513. }
  514. }
  515. //
  516. // Initialize remaining fields.
  517. //
  518. if (newThread != NULL) {
  519. newThread->_exitFlag = FALSE;
  520. newThread->_pendingRequest = NULL;
  521. newThread->_pool = this;
  522. memset(&newThread->_listEntry, 0, sizeof(LIST_ENTRY));
  523. }
  524. //
  525. // Create the unsuspended background thread.
  526. //
  527. if (newThread != NULL) {
  528. newThread->_threadHandle =
  529. CreateThread(
  530. NULL, 0,
  531. (LPTHREAD_START_ROUTINE)ThreadPool::_PooledThread,
  532. newThread, 0, &newThread->_tid
  533. );
  534. if (newThread->_threadHandle == NULL) {
  535. TRC_ERR((TB, _T("Can't create thread: %08X."), GetLastError()));
  536. CloseHandle(newThread->_synchronizationEvent);
  537. delete newThread;
  538. newThread = NULL;
  539. }
  540. else {
  541. TRC_NRM((TB, _T("Successfully created thread %ld."), newThread->_tid));
  542. }
  543. }
  544. //
  545. // If we successfully created a new thread, then add it to the list.
  546. //
  547. if (newThread != NULL) {
  548. InsertHeadList(&_threadListHead, &newThread->_listEntry);
  549. _threadCount++;
  550. }
  551. //
  552. // Unlock and return.
  553. //
  554. Unlock();
  555. DC_END_FN();
  556. return newThread;
  557. }
  558. DWORD
  559. ThreadPool::_PooledThread(
  560. IN PTHREADPOOL_THREAD thr
  561. )
  562. /*++
  563. Routine Description:
  564. Static Pooled Thread Function
  565. Arguments:
  566. Windows Error Code
  567. Return Value:
  568. The new thread. NULL if unable to create a new thread.
  569. --*/
  570. {
  571. DC_BEGIN_FN("ThreadPool::_PooledThread");
  572. //
  573. // Call the instance-specific function.
  574. //
  575. DC_END_FN();
  576. return thr->_pool->PooledThread(thr);
  577. }
  578. DWORD
  579. ThreadPool::PooledThread(
  580. IN PTHREADPOOL_THREAD thr
  581. )
  582. /*++
  583. Routine Description:
  584. Pooled Thread Function
  585. Arguments:
  586. Windows Error Code
  587. Return Value:
  588. The new thread. NULL if unable to create a new thread.
  589. --*/
  590. {
  591. BOOL done;
  592. DWORD result;
  593. BOOL cleanUpThread = FALSE;
  594. DC_BEGIN_FN("ThreadPool::PooledThread");
  595. done = FALSE;
  596. while (!done) {
  597. //
  598. // Wait for the synchronization event to fire.
  599. //
  600. result = WaitForSingleObject(thr->_synchronizationEvent, INFINITE);
  601. //
  602. // See if the exit flag is set.
  603. //
  604. if (thr->_exitFlag) {
  605. TRC_NRM((TB, _T("Thread %p: exit flag set. Exiting thread."), thr));
  606. done = TRUE;
  607. }
  608. else if (result == WAIT_OBJECT_0) {
  609. //
  610. // See if there is a request pending.
  611. //
  612. if (thr->_pendingRequest != NULL) {
  613. TRC_NRM((TB, _T("Thread %ld: processing new request."), thr->_tid));
  614. HandlePendingRequest(thr);
  615. //
  616. // See if the exit flag is set.
  617. //
  618. if (thr->_exitFlag) {
  619. TRC_NRM((TB, _T("Thread %p: exit flag set. Exiting thread."), thr));
  620. done = TRUE;
  621. break;
  622. }
  623. //
  624. // If we have more threads than the minimum, then remove this
  625. // thread from the list and exit.
  626. //
  627. if (GetThreadCount() > _minThreads) {
  628. TRC_NRM((TB,
  629. _T("Thread %ld: count %ld is greater than min threads %ld."),
  630. thr->_tid, GetThreadCount(), _minThreads)
  631. );
  632. Lock();
  633. if ((thr->_listEntry.Blink != NULL) &&
  634. (thr->_listEntry.Flink != NULL)) {
  635. RemoveEntryList(&thr->_listEntry);
  636. thr->_listEntry.Flink = NULL;
  637. thr->_listEntry.Blink = NULL;
  638. }
  639. cleanUpThread = TRUE;
  640. _threadCount--;
  641. Unlock();
  642. done = TRUE;
  643. }
  644. //
  645. // Reset the pending request value.
  646. //
  647. thr->_pendingRequest = NULL;
  648. }
  649. }
  650. else {
  651. TRC_ERR((TB, _T("Thread %ld: WaitForSingleObject: %08X."), thr->_tid,
  652. GetLastError()));
  653. done = TRUE;
  654. }
  655. }
  656. TRC_NRM((TB, _T("Thread %ld is shutting down."), thr->_tid));
  657. //
  658. // Release the data structure associated with this thread if
  659. // we should.
  660. //
  661. if (cleanUpThread) {
  662. delete thr;
  663. }
  664. DC_END_FN();
  665. return 0;
  666. }
  667. VOID
  668. ThreadPool::HandlePendingRequest(
  669. IN PTHREADPOOL_THREAD thr
  670. )
  671. /*++
  672. Routine Description:
  673. Call the function associated with a pending thread request.
  674. Arguments:
  675. thr - Relevent thread.
  676. Return Value:
  677. NA
  678. --*/
  679. {
  680. DC_BEGIN_FN("ThreadPool::PooledThread");
  681. thr->_pendingRequest->_completionStatus =
  682. thr->_pendingRequest->_func(thr->_pendingRequest->_clientData, thr->_synchronizationEvent);
  683. Lock();
  684. if (thr->_pendingRequest->_completionEvent != NULL) {
  685. SetEvent(thr->_pendingRequest->_completionEvent);
  686. }
  687. Unlock();
  688. DC_END_FN();
  689. }
  690. ///////////////////////////////////////////////////////////////
  691. //
  692. // Unit-Test Functions that Tests Thread Pools in the Background
  693. //
  694. #if DBG
  695. DWORD ThrTstBackgroundThread(PVOID tag);
  696. #define THRTST_MAXSLEEPINTERVAL 2000
  697. #define THRTST_MAXFUNCINTERVAL 1000
  698. #define THRTST_THREADTIMEOUT 60000
  699. #define THRTST_THREADRETURNVALUE 0x565656
  700. #define THRTST_MAXTSTTHREADS 5
  701. #define THRTST_MAXBACKGROUNDTHREADS 5
  702. #define THRTST_MINPOOLTHREADS 3
  703. #define THRTST_MAXPOOLTHREADS 7
  704. #define THRTST_CLIENTDATA 0x787878
  705. ThreadPool *ThrTstPool = NULL;
  706. HANDLE ThrTstShutdownEvent = NULL;
  707. HANDLE ThrTstThreadHandles[THRTST_MAXBACKGROUNDTHREADS];
  708. void ThreadPoolTestInit()
  709. {
  710. DWORD tid;
  711. ULONG i;
  712. DC_BEGIN_FN("ThreadPoolTestInit");
  713. //
  714. // Create the pool.
  715. //
  716. ThrTstPool = new ThreadPool(THRTST_MINPOOLTHREADS,
  717. THRTST_MAXPOOLTHREADS);
  718. if (ThrTstPool == NULL) {
  719. TRC_ERR((TB, _T("Can't allocate thread pool")));
  720. return;
  721. }
  722. ThrTstPool->Initialize();
  723. //
  724. // Create the shutdown event.
  725. //
  726. ThrTstShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  727. if (ThrTstShutdownEvent == NULL) {
  728. TRC_ERR((TB, _T("Can't create shutdown event: %08X"),
  729. GetLastError()));
  730. return;
  731. }
  732. //
  733. // Spawn the background threads for this test.
  734. //
  735. for (i=0; i<THRTST_MAXBACKGROUNDTHREADS; i++) {
  736. ThrTstThreadHandles[i] =
  737. CreateThread(
  738. NULL, 0,
  739. (LPTHREAD_START_ROUTINE)ThrTstBackgroundThread,
  740. (PVOID)THRTST_CLIENTDATA, 0, &tid
  741. );
  742. if (ThrTstThreadHandles[i] == NULL) {
  743. TRC_ERR((TB, _T("Can't spin off background thread: %08X"),
  744. GetLastError()));
  745. ASSERT(FALSE);
  746. }
  747. }
  748. DC_END_FN();
  749. }
  750. void ThreadPoolTestShutdown()
  751. {
  752. ULONG i;
  753. DC_BEGIN_FN("ThreadPoolTestShutdown");
  754. //
  755. // Signal the background thread to shut down.
  756. //
  757. if (ThrTstShutdownEvent != NULL) {
  758. SetEvent(ThrTstShutdownEvent);
  759. }
  760. TRC_NRM((TB, _T("Waiting for background thread to exit.")));
  761. //
  762. // Wait for the background threads to shut down.
  763. //
  764. for (i=0; i<THRTST_MAXBACKGROUNDTHREADS; i++) {
  765. if (ThrTstThreadHandles[i] != NULL) {
  766. DWORD result = WaitForSingleObject(
  767. ThrTstThreadHandles[i],
  768. THRTST_THREADTIMEOUT
  769. );
  770. if (result != WAIT_OBJECT_0) {
  771. DebugBreak();
  772. }
  773. }
  774. }
  775. TRC_NRM((TB, _T("Background threads exited.")));
  776. //
  777. // Close the thread pool.
  778. //
  779. if (ThrTstPool != NULL) {
  780. delete ThrTstPool;
  781. }
  782. //
  783. // Clean up the shut down event.
  784. //
  785. if (ThrTstShutdownEvent != NULL) {
  786. CloseHandle(ThrTstShutdownEvent);
  787. }
  788. ThrTstShutdownEvent = NULL;
  789. ThrTstPool = NULL;
  790. DC_END_FN();
  791. }
  792. DWORD ThrTstFunction(PVOID clientData, HANDLE cancelEvent)
  793. {
  794. DC_BEGIN_FN("ThrTstFunction");
  795. UNREFERENCED_PARAMETER(clientData);
  796. UNREFERENCED_PARAMETER(cancelEvent);
  797. //
  798. // Do "something" for a random amount of time.
  799. //
  800. int interval = (rand() % THRTST_MAXFUNCINTERVAL)+1;
  801. Sleep(interval);
  802. return THRTST_THREADRETURNVALUE;
  803. DC_END_FN();
  804. }
  805. DWORD ThrTstBackgroundThread(PVOID tag)
  806. {
  807. ULONG count, i;
  808. HANDLE events[THRTST_MAXTSTTHREADS];
  809. ThreadPoolRequest requests[THRTST_MAXTSTTHREADS];
  810. DC_BEGIN_FN("ThrTstBackgroundThread");
  811. ASSERT(tag == (PVOID)THRTST_CLIENTDATA);
  812. //
  813. // Create function completion events.
  814. //
  815. for (i=0; i<THRTST_MAXTSTTHREADS; i++) {
  816. events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
  817. if (events[i] == NULL) {
  818. TRC_ERR((TB, _T("Error creating function complete event.")));
  819. ASSERT(FALSE);
  820. return -1;
  821. }
  822. }
  823. //
  824. // Loop until the shutdown event has been fired.
  825. //
  826. while (WaitForSingleObject(ThrTstShutdownEvent,
  827. THRTST_MAXSLEEPINTERVAL) == WAIT_TIMEOUT) {
  828. //
  829. // Spin a random number of requests and wait for them
  830. // to finish.
  831. //
  832. count = (rand()%THRTST_MAXTSTTHREADS)+1;
  833. for (i=0; i<count; i++) {
  834. TRC_NRM((TB, _T("Submitting next request.")));
  835. ResetEvent(events[i]);
  836. requests[i] = ThrTstPool->SubmitRequest(
  837. ThrTstFunction,
  838. (PVOID)THRTST_CLIENTDATA,
  839. events[i]
  840. );
  841. }
  842. //
  843. // Make sure the client data looks good.
  844. //
  845. for (i=0; i<count; i++) {
  846. TRC_NRM((TB, _T("Checking client data.")));
  847. if (requests[i] != INVALID_THREADPOOLREQUEST) {
  848. ASSERT(
  849. ThrTstPool->GetRequestClientData(
  850. requests[i]) == (PVOID)THRTST_CLIENTDATA
  851. );
  852. }
  853. }
  854. //
  855. // Wait for all the requests to finish.
  856. //
  857. for (i=0; i<count; i++) {
  858. TRC_NRM((TB, _T("Waiting for IO to complete.")));
  859. if (requests[i] != INVALID_THREADPOOLREQUEST) {
  860. DWORD result = WaitForSingleObject(events[i], INFINITE);
  861. ASSERT(result == WAIT_OBJECT_0);
  862. }
  863. }
  864. //
  865. // Make sure the return status is correct.
  866. //
  867. for (i=0; i<count; i++) {
  868. TRC_NRM((TB, _T("Checking return status.")));
  869. if (requests[i] != INVALID_THREADPOOLREQUEST) {
  870. ASSERT(
  871. ThrTstPool->GetRequestCompletionStatus(requests[i])
  872. == THRTST_THREADRETURNVALUE
  873. );
  874. }
  875. }
  876. //
  877. // Close the requests.
  878. //
  879. for (i=0; i<count; i++) {
  880. TRC_NRM((TB, _T("Closing requests.")));
  881. if (requests[i] != INVALID_THREADPOOLREQUEST) {
  882. ThrTstPool->CloseRequest(requests[i]);
  883. }
  884. }
  885. }
  886. TRC_NRM((TB, _T("Shutdown flag detected.")));
  887. DC_END_FN();
  888. return 0;
  889. }
  890. #endif