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.

1007 lines
28 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: QQueue.cxx
  7. //
  8. // Contents: Query queue
  9. //
  10. // History: 29-Dec-93 KyleP Created
  11. //
  12. //--------------------------------------------------------------------------
  13. #include <pch.cxx>
  14. #pragma hdrstop
  15. #include <worker.hxx>
  16. #include <ciregkey.hxx>
  17. #include <regacc.hxx>
  18. #include <imprsnat.hxx>
  19. extern BOOL g_fPerfmonCounterHackIsProcessDetached;
  20. //+-------------------------------------------------------------------------
  21. //
  22. // Member: CWorkQueue::GetWorkQueueRegParams, public
  23. //
  24. // Synopsis: Fetches registry params for the work queue
  25. //
  26. // History: 30-Dec-96 SrikantS Moved the code from RefreshRegParams
  27. //
  28. //--------------------------------------------------------------------------
  29. void CWorkQueue::GetWorkQueueRegParams( ULONG & cMaxActiveThreads,
  30. ULONG & cMinIdleThreads )
  31. {
  32. CRegAccess reg( RTL_REGISTRY_CONTROL, wcsRegAdmin );
  33. if ( CWorkQueue::workQueueQuery == _QueueType )
  34. {
  35. cMaxActiveThreads = reg.Read( wcsMaxActiveQueryThreads,
  36. CI_MAX_ACTIVE_QUERY_THREADS_DEFAULT,
  37. CI_MAX_ACTIVE_QUERY_THREADS_MIN,
  38. CI_MAX_ACTIVE_QUERY_THREADS_MAX );
  39. cMinIdleThreads = reg.Read( wcsMinIdleQueryThreads,
  40. CI_MIN_IDLE_QUERY_THREADS_DEFAULT,
  41. CI_MIN_IDLE_QUERY_THREADS_MIN,
  42. CI_MIN_IDLE_QUERY_THREADS_MAX );
  43. }
  44. else if ( CWorkQueue::workQueueRequest == _QueueType )
  45. {
  46. cMaxActiveThreads = reg.Read( wcsMaxActiveRequestThreads,
  47. CI_MAX_ACTIVE_REQUEST_THREADS_DEFAULT,
  48. CI_MAX_ACTIVE_REQUEST_THREADS_MIN,
  49. CI_MAX_ACTIVE_REQUEST_THREADS_MAX );
  50. cMinIdleThreads = reg.Read( wcsMinIdleRequestThreads,
  51. CI_MIN_IDLE_REQUEST_THREADS_DEFAULT,
  52. CI_MIN_IDLE_REQUEST_THREADS_MIN,
  53. CI_MIN_IDLE_REQUEST_THREADS_MAX );
  54. }
  55. else
  56. {
  57. cMaxActiveThreads = 2;
  58. cMinIdleThreads = 0;
  59. }
  60. } //GetWorkQueueRegParams
  61. //+-------------------------------------------------------------------------
  62. //
  63. // Member: CWorkQueue::CWorkQueue, public
  64. //
  65. // Synopsis: Initialize queue.
  66. //
  67. // Arguments: [cThread] -- Number of worker threads.
  68. // [workQueue] -- Work queue type for getting registry settings.
  69. //
  70. // History: 29-Dec-93 KyleP Created
  71. //
  72. //--------------------------------------------------------------------------
  73. CWorkQueue::CWorkQueue(
  74. unsigned cThread,
  75. WorkQueueType workQueue )
  76. : _cItems( 0 ),
  77. _pHead( 0 ),
  78. _pTail( 0 ),
  79. _apWorker( 0 ), // avoid allocation in construction of global
  80. _pIdle( 0 ),
  81. _cIdle( 0 ),
  82. _fInit( FALSE ),
  83. _fAbort( FALSE ),
  84. _cWorker( 0 ),
  85. _cMaxActiveThreads(cThread),
  86. _cMinIdleThreads(0),
  87. _QueueType( workQueue ),
  88. _mtxLock( FALSE )
  89. {
  90. Win4Assert( workQueueQuery == workQueue ||
  91. workQueueRequest == workQueue ||
  92. workQueueFrmwrkClient == workQueue );
  93. // Note: don't call RefreshRegParams as long as the work queue
  94. // is a global object -- there's no telling what security context will
  95. // be used to load the .dll.
  96. //RefreshParams( cThread, cThread );
  97. } //CWorkQueue
  98. //+-------------------------------------------------------------------------
  99. //
  100. // Member: CWorkQueue::~CWorkQueue, public
  101. //
  102. // Synopsis: Waits for queue threads to stop.
  103. //
  104. // Requires: An external source has killed all queue items.
  105. //
  106. // History: 29-Dec-93 KyleP Created
  107. //
  108. //--------------------------------------------------------------------------
  109. CWorkQueue::~CWorkQueue()
  110. {
  111. Win4Assert( _cItems == 0 );
  112. if ( _fInit )
  113. {
  114. // If somehow we're unwinding a global object after the heap has
  115. // been destroyed, don't do any work.
  116. if ( g_fPerfmonCounterHackIsProcessDetached )
  117. {
  118. // Don't free this since the heap is already gone!
  119. _apWorker.AcquireAll();
  120. return;
  121. }
  122. Shutdown();
  123. }
  124. } //~CWorkQueue
  125. //+-------------------------------------------------------------------------
  126. //
  127. // Member: CWorkQueue::RefreshParams, public
  128. //
  129. // Synopsis: Refreshes registry params for the work queue
  130. //
  131. // History: 7-Nov-96 dlee Created
  132. // 30-Dec-96 SrikantS Changed name to RefreshParams and removed
  133. // the dependency on registry.
  134. //
  135. //--------------------------------------------------------------------------
  136. #pragma warning(push)
  137. #pragma warning(disable:4296)
  138. void CWorkQueue::RefreshParams( ULONG cMaxActiveThread,
  139. ULONG cMinIdleThread )
  140. {
  141. CLock lock( _mtxLock );
  142. if ( CWorkQueue::workQueueQuery == _QueueType )
  143. {
  144. _cMaxActiveThreads = min( CI_MAX_ACTIVE_QUERY_THREADS_MAX,
  145. max(CI_MAX_ACTIVE_QUERY_THREADS_MIN,
  146. cMaxActiveThread) );
  147. _cMinIdleThreads = min( CI_MIN_IDLE_QUERY_THREADS_MAX,
  148. max(CI_MIN_IDLE_QUERY_THREADS_MIN,
  149. cMinIdleThread) );
  150. }
  151. else if ( CWorkQueue::workQueueRequest == _QueueType )
  152. {
  153. _cMaxActiveThreads = min( CI_MAX_ACTIVE_REQUEST_THREADS_MAX,
  154. max(CI_MAX_ACTIVE_REQUEST_THREADS_MIN,
  155. cMaxActiveThread) );
  156. _cMinIdleThreads = min( CI_MIN_IDLE_REQUEST_THREADS_MAX,
  157. max(CI_MIN_IDLE_REQUEST_THREADS_MIN,
  158. cMinIdleThread) );
  159. }
  160. else // framework client queue
  161. {
  162. _cMaxActiveThreads = 2;
  163. _cMinIdleThreads = 0;
  164. }
  165. } //RefreshParams
  166. #pragma warning(pop)
  167. //+-------------------------------------------------------------------------
  168. //
  169. // Member: CWorkQueue::Shutdown, public
  170. //
  171. // Synopsis: Shuts down the work queue
  172. //
  173. // History: 7-Nov-96 dlee Added header
  174. //
  175. //--------------------------------------------------------------------------
  176. void CWorkQueue::Shutdown()
  177. {
  178. if (_fInit)
  179. {
  180. CLock lock( _mtxLock );
  181. _fAbort = TRUE;
  182. for ( CWorkThread * pw = _pIdle; pw; pw = pw->Next() )
  183. {
  184. Win4Assert(pw != pw->Next());
  185. pw->Wakeup();
  186. }
  187. vqDebugOut(( DEB_ITRACE, "Work queue shutdown: woke up threads\n" ));
  188. }
  189. _apWorker.Free();
  190. _cWorker = 0;
  191. vqDebugOut(( DEB_ITRACE, "Work queue shutdown: exiting\n" ));
  192. } //Shutdown
  193. //+-------------------------------------------------------------------------
  194. //
  195. // Member: CWorkQueue::AddRefWorkThreads, public
  196. //
  197. // Synopsis: Addrefs the current set of worker threads
  198. //
  199. // History: 21-May-97 dlee Created
  200. //
  201. //--------------------------------------------------------------------------
  202. void CWorkQueue::AddRefWorkThreads()
  203. {
  204. CLock lock( _mtxLock );
  205. for ( ULONG x = 0; x < _cWorker; x++ )
  206. _apWorker[x]->AddRef();
  207. } //AddRefWorkThreads
  208. //+-------------------------------------------------------------------------
  209. //
  210. // Member: CWorkQueue::ReleaseWorkThreads, public
  211. //
  212. // Synopsis: Releases the current set of worker threads
  213. //
  214. // History: 21-May-97 dlee Created
  215. //
  216. //--------------------------------------------------------------------------
  217. void CWorkQueue::ReleaseWorkThreads()
  218. {
  219. CLock lock( _mtxLock );
  220. for ( ULONG x = 0; x < _cWorker; x++ )
  221. _apWorker[x]->Release();
  222. } //ReleaseWorkThreads
  223. //+-------------------------------------------------------------------------
  224. //
  225. // Member: CWorkQueue::Add, public
  226. //
  227. // Synopsis: Add a query to the queue.
  228. //
  229. // Arguments: [pitem] -- Query to add
  230. //
  231. // History: 29-Dec-93 KyleP Created
  232. //
  233. //--------------------------------------------------------------------------
  234. void CWorkQueue::Add( PWorkItem * pitem )
  235. {
  236. BOOL fTryRemove = FALSE;
  237. {
  238. CLock lock( _mtxLock );
  239. if ( _fAbort )
  240. {
  241. vqDebugOut(( DEB_ITRACE, "work queue, shutdown, so not adding item\n" ));
  242. return;
  243. }
  244. pitem->AddRef();
  245. //
  246. // Insert at end of list
  247. //
  248. if ( _pTail )
  249. {
  250. _pTail->Link( pitem );
  251. _pTail = pitem;
  252. }
  253. else
  254. {
  255. Win4Assert( !_pHead );
  256. _pHead = pitem;
  257. _pTail = pitem;
  258. }
  259. _pTail->Link( 0 );
  260. _cItems++;
  261. vqDebugOut(( DEB_ITRACE, "Work queue: add 0x%x\n", pitem ));
  262. //
  263. // Add a new thread if there are no idle ones (or ones processing APCs)
  264. //
  265. if ( ( ( _cWorker == (unsigned) _cInAPC ) || ( 0 == _pIdle ) ) &&
  266. ( _cWorker < _cMaxActiveThreads ) )
  267. {
  268. TRY
  269. {
  270. lokAddThread();
  271. }
  272. CATCH( CException, e )
  273. {
  274. //
  275. // Remove the item from the work queue so it gets released
  276. // properly.
  277. //
  278. Remove( pitem );
  279. //
  280. // Now rethrow the exception, which will only happen if
  281. // there are not worker threads at all.
  282. //
  283. RETHROW();
  284. }
  285. END_CATCH;
  286. Win4Assert( _pIdle == 0 || _pIdle->Next() != _pIdle );
  287. }
  288. //
  289. // Wake up a thread if there is an idle one.
  290. //
  291. if ( _pIdle )
  292. {
  293. Win4Assert( _cIdle > 0 );
  294. // Move any thread not processing an APC to the start of the list
  295. CWorkThread * pwPrev = 0;
  296. for ( CWorkThread * pw = _pIdle; 0 != pw; pw = pw->Next() )
  297. {
  298. if ( !pw->IsProcessingAPC() )
  299. {
  300. if ( pw != _pIdle )
  301. {
  302. Win4Assert( 0 != pwPrev );
  303. pwPrev->Link( pw->Next() );
  304. pw->Link( _pIdle );
  305. _pIdle = pw;
  306. }
  307. break;
  308. }
  309. pwPrev = pw;
  310. }
  311. CWorkThread * pWorker = _pIdle;
  312. _pIdle = _pIdle->Next();
  313. _cIdle--;
  314. Win4Assert( _pIdle != pWorker );
  315. pWorker->Link( 0 );
  316. pWorker->Wakeup();
  317. if ( _cIdle > _cMinIdleThreads )
  318. fTryRemove = TRUE;
  319. }
  320. }
  321. if ( fTryRemove )
  322. RemoveThreads();
  323. } //Add
  324. //+-------------------------------------------------------------------------
  325. //
  326. // Member: CWorkQueue::Release, public
  327. //
  328. // Synopsis: Release refcount on worker thread
  329. //
  330. // Arguments: [pThread] -- Worker thread
  331. //
  332. // History: 13-Mar-96 KyleP Created
  333. //
  334. //--------------------------------------------------------------------------
  335. void CWorkQueue::Release( CWorkThread * pThread )
  336. {
  337. pThread->Release();
  338. BOOL fTryRemove = FALSE;
  339. if ( GetCurrentThreadId() != pThread->GetThreadId() )
  340. {
  341. CLock lock( _mtxLock );
  342. if ( _pIdle )
  343. {
  344. Win4Assert( _cIdle > 0 );
  345. if ( _cIdle > _cMinIdleThreads )
  346. fTryRemove = TRUE;
  347. }
  348. }
  349. if ( fTryRemove )
  350. RemoveThreads();
  351. } //Release
  352. //+-------------------------------------------------------------------------
  353. //
  354. // Member: CWorkQueue::Remove, private
  355. //
  356. // Synopsis: Remove a query from the queue.
  357. //
  358. // Arguments: [Worker] -- Worker thread to own the query
  359. //
  360. // History: 29-Dec-93 KyleP Created
  361. //
  362. // Notes: Remove is used by worker threads to acquire a new work
  363. // item. In contrast, the other remove will just remove
  364. // an item from the queue.
  365. //
  366. //--------------------------------------------------------------------------
  367. void CWorkQueue::Remove( CWorkThread & Worker )
  368. {
  369. Win4Assert( Worker.ActiveItem() == 0 );
  370. for (;;)
  371. {
  372. //
  373. // Look for an item
  374. //
  375. {
  376. CLock lock( _mtxLock );
  377. Worker.Reset();
  378. //
  379. // We may have been awoken by an APC. Be sure we're not
  380. // still on the idle queue in this case.
  381. //
  382. if ( _pIdle )
  383. {
  384. if ( _pIdle == &Worker )
  385. {
  386. _cIdle--;
  387. _pIdle = Worker.Next();
  388. }
  389. else
  390. {
  391. for ( CWorkThread * pw = _pIdle; pw; pw = pw->Next() ) {
  392. if (pw->Next() == &Worker)
  393. {
  394. _cIdle--;
  395. pw->Link(Worker.Next());
  396. break;
  397. }
  398. }
  399. }
  400. }
  401. if ( _fAbort || Worker.lokShouldAbort() )
  402. {
  403. vqDebugOut(( DEB_ITRACE,
  404. "Work queue: abort worker thread.\n" ));
  405. break;
  406. }
  407. if ( Count() > 0 )
  408. {
  409. vqDebugOut(( DEB_ITRACE,
  410. "Work queue: %d pending\n", _cItems ));
  411. Worker.lokSetActiveItem( _pHead );
  412. _pHead = _pHead->Next();
  413. if ( _pHead == 0 )
  414. _pTail = 0;
  415. _cItems--;
  416. break;
  417. }
  418. //
  419. // Nothing on the queue right now. Wait for item.
  420. //
  421. Worker.Link( _pIdle );
  422. _pIdle = &Worker;
  423. _cIdle++;
  424. Win4Assert(Worker.Next() != &Worker);
  425. }
  426. Worker.Wait();
  427. }
  428. vqDebugOut(( DEB_ITRACE, "Work queue: remove 0x%x\n", Worker.ActiveItem() ));
  429. //
  430. // We're not sleeping the thread, make sure we
  431. // process APCs.
  432. //
  433. SleepEx( 0, TRUE );
  434. } //Remove
  435. //+-------------------------------------------------------------------------
  436. //
  437. // Member: CWorkQueue::Remove, public
  438. //
  439. // Effects: Deletes all references to [pitem] from the queue. If
  440. // pitem is a query-in-progress in a worker thread then
  441. // we wait until that query completes before returning from
  442. // Delete.
  443. //
  444. // Arguments: [pitem] -- Query to remove
  445. //
  446. // History: 29-Dec-93 KyleP Created
  447. // 09-Feb-94 KyleP Speed up removal of active item
  448. //
  449. // Notes: The item may still be running on a worker thread when we
  450. // return from this call.
  451. //
  452. //--------------------------------------------------------------------------
  453. void CWorkQueue::Remove( PWorkItem * pitem )
  454. {
  455. CLock lock( _mtxLock );
  456. if ( _pHead == pitem )
  457. {
  458. _pHead = pitem->Next();
  459. if ( _pTail == pitem )
  460. {
  461. Win4Assert( _pHead == 0 );
  462. _pTail = 0;
  463. }
  464. _cItems--;
  465. pitem->Release();
  466. vqDebugOut(( DEB_ITRACE,
  467. "Work queue: delete 0x%x\n", pitem ));
  468. }
  469. else
  470. {
  471. for ( PWorkItem * pCurrent = _pHead;
  472. pCurrent && pCurrent->Next() != pitem;
  473. pCurrent = pCurrent->Next() )
  474. continue; // Null body
  475. if ( pCurrent )
  476. {
  477. Win4Assert( pCurrent->Next() == pitem );
  478. if ( _pTail == pitem )
  479. {
  480. _pTail = pCurrent;
  481. }
  482. pCurrent->Link( pitem->Next() );
  483. _cItems--;
  484. pitem->Release();
  485. vqDebugOut(( DEB_ITRACE,
  486. "Work queue: delete 0x%x\n", pitem ));
  487. }
  488. }
  489. } //Remove
  490. //+-------------------------------------------------------------------------
  491. //
  492. // Member: CWorkQueue::AddThread, private
  493. //
  494. // Synopsis: Adds new worker thread to idle worker list.
  495. //
  496. // History: 12-Jan-94 KyleP Created
  497. //
  498. //--------------------------------------------------------------------------
  499. void CWorkQueue::lokAddThread()
  500. {
  501. TRY
  502. {
  503. vqDebugOut(( DEB_ITRACE, "Add worker thread %d of %d\n",
  504. _cWorker+1, _apWorker.Size() ));
  505. //
  506. // Worker threads must be created in the system context or they
  507. // won't have the permission to revert to system, which is needed
  508. // for queries on remote volumes and to write to catalog files.
  509. //
  510. CImpersonateSystem impersonate;
  511. CEventSem evt1;
  512. SHandle hEvt1( evt1.AcquireHandle() );
  513. CEventSem evt2;
  514. SHandle hEvt2( evt2.AcquireHandle() );
  515. _apWorker.Add( new CWorkThread( *this,
  516. _pIdle,
  517. hEvt1,
  518. hEvt2 ),
  519. _cWorker );
  520. _pIdle = _apWorker[_cWorker];
  521. _cIdle++;
  522. _cWorker++;
  523. }
  524. CATCH( CException, e )
  525. {
  526. vqDebugOut(( DEB_ERROR,
  527. "Exception 0x%x caught creating query worker threads."
  528. " Running with %d threads.\n", e.GetErrorCode(), _cWorker ));
  529. // Only throw an excption if there are no threads around at all.
  530. // If there is at least 1 thread it'll eventually get to the work
  531. // items.
  532. if ( 0 == _cWorker )
  533. {
  534. RETHROW();
  535. }
  536. }
  537. END_CATCH
  538. Win4Assert( _cWorker != 0 );
  539. } //lokAddThread
  540. //+-------------------------------------------------------------------------
  541. //
  542. // Member: CWorkQueue::RemoveThreads, private
  543. //
  544. // Synopsis: Removes one or more threads from idle queue.
  545. //
  546. // History: 28-Sep-95 KyleP Created
  547. //
  548. //--------------------------------------------------------------------------
  549. const DWORD tenSeconds = 1000 * 10;
  550. void CWorkQueue::RemoveThreads()
  551. {
  552. while ( TRUE )
  553. {
  554. CWorkThread * pThread = 0;
  555. //
  556. // Check for too many idle threads under lock.
  557. //
  558. {
  559. CLock lock( _mtxLock );
  560. if ( _cIdle > _cMinIdleThreads )
  561. {
  562. //
  563. // Find an idle thread that has a refcount of 0 and hasn't
  564. // done anything in 10 seconds.
  565. //
  566. CWorkThread * pPrev = 0;
  567. for ( pThread = _pIdle; 0 != pThread; pThread = pThread->Next() )
  568. {
  569. if ( !pThread->IsReferenced() )
  570. {
  571. vqDebugOut(( DEB_ITRACE, "isidle? %d\n", pThread->IsIdleFor( tenSeconds ) ));
  572. if ( pThread->IsIdleFor( tenSeconds ) )
  573. break;
  574. }
  575. pPrev = pThread;
  576. }
  577. if ( 0 != pThread )
  578. {
  579. for ( unsigned i = 0; i < _cWorker; i++ )
  580. {
  581. //
  582. // Remove first idle thread from all queues and abort it.
  583. //
  584. if ( _apWorker[i] == pThread )
  585. {
  586. vqDebugOut(( DEB_ITRACE, "Deleting extra idle thread\n" ));
  587. if ( 0 == pPrev )
  588. _pIdle = _pIdle->Next();
  589. else
  590. pPrev->Link( pThread->Next() );
  591. _cIdle--;
  592. _cWorker--;
  593. _apWorker.Acquire( i );
  594. _apWorker.Add( _apWorker.Acquire(_cWorker), i );
  595. pThread->lokAbort();
  596. pThread->Wakeup();
  597. break;
  598. }
  599. }
  600. Win4Assert( i <= _cWorker );
  601. }
  602. }
  603. }
  604. //
  605. // If we found an extra idle thread, delete it and try again, else just get out.
  606. //
  607. if ( pThread )
  608. delete pThread;
  609. else
  610. break;
  611. }
  612. } //RemoveThreads
  613. //+-------------------------------------------------------------------------
  614. //
  615. // Member: CWorkThread::WorkerThread, static private
  616. //
  617. // Synopsis: Main loop that executes query.
  618. //
  619. // Arguments: [self] -- this pointer for CWorkThread object
  620. //
  621. // History: 29-Dec-93 KyleP Created
  622. //
  623. //--------------------------------------------------------------------------
  624. DWORD WINAPI CWorkThread::WorkerThread( void * self )
  625. {
  626. CWorkThread * pWorker = (CWorkThread *)self;
  627. for( pWorker->_queue.Remove( *pWorker );
  628. pWorker->ActiveItem();
  629. pWorker->_queue.Remove( *pWorker ) )
  630. {
  631. TRY
  632. {
  633. pWorker->ActiveItem()->DoIt( pWorker );
  634. }
  635. CATCH( CException, e )
  636. {
  637. vqDebugOut(( DEB_ERROR, "pWorker->DoIt() failed with error 0x%x\n", e.GetErrorCode() ));
  638. }
  639. END_CATCH
  640. pWorker->Done();
  641. }
  642. //
  643. // We should not do an ExitThread() here because there will be a deadlock
  644. // during shutdown. The DLL_PROCESS_DETACH is called with the "LoaderLock"
  645. // CriticalSection held by the LdrUnloadDll() during the DLL detach.
  646. // Terminating the thread here is okay because there is no cleanup to be
  647. // done after this.
  648. //
  649. vqDebugOut(( DEB_ITRACE, "WorkerThread 0x%x: exiting\n", self ));
  650. return 0;
  651. } //WorkerThread
  652. //+-------------------------------------------------------------------------
  653. //
  654. // Member: CWorkThread::CWorkThread, public
  655. //
  656. // Synopsis: Creates worker thread.
  657. //
  658. // Arguments: [queue] -- Work queue which owns worker.
  659. // [pNext] -- Link. Used by Work queue.
  660. // [hEvt1] -- Event handle for first event in the worker thread
  661. // [hEvt2] -- Event handle for second event in the worker thread
  662. //
  663. // History: 29-Dec-93 KyleP Created
  664. //
  665. //--------------------------------------------------------------------------
  666. #pragma warning( disable : 4355 ) // this used in base initialization
  667. CWorkThread::CWorkThread( CWorkQueue & queue,
  668. CWorkThread * pNext
  669. , SHandle & hEvt1
  670. , SHandle & hEvt2
  671. )
  672. : _queue( queue ),
  673. _pNext( pNext ),
  674. _pitem( 0 ),
  675. _fAbort( FALSE ),
  676. _Thread( CWorkThread::WorkerThread, this, TRUE ),
  677. _cRef( 0 )
  678. , _evtQueryAvailable( hEvt1.Acquire() )
  679. , _evtDone( hEvt2.Acquire() ),
  680. _pDeferredAPCs( 0 ),
  681. _fProcessingAPC( FALSE ),
  682. _dwLastUsed( GetTickCount() )
  683. {
  684. _Thread.Resume();
  685. }
  686. #pragma warning( default : 4355 )
  687. //+-------------------------------------------------------------------------
  688. //
  689. // Member: CWorkThread::~CWorkThread, public
  690. //
  691. // Synopsis: Waits for thread to die.
  692. //
  693. // History: 29-Dec-93 KyleP Created
  694. //
  695. //--------------------------------------------------------------------------
  696. CWorkThread::~CWorkThread()
  697. {
  698. vqDebugOut(( DEB_ITRACE, "Worker 0x%x: WAIT for death\n", this ));
  699. Win4Assert( GetCurrentThreadId() != GetThreadId() );
  700. _Thread.WaitForDeath();
  701. vqDebugOut(( DEB_ITRACE, "Worker 0x%x: done waiting for death\n", this ));
  702. }
  703. //+-------------------------------------------------------------------------
  704. //
  705. // Member: CWorkThread::ProcessDeferredAPCs, private
  706. //
  707. // Synopsis: Processes all deferred APCs
  708. //
  709. // History: 17-Jul-00 dlee Created
  710. //
  711. //--------------------------------------------------------------------------
  712. void CWorkThread::ProcessDeferredAPCs()
  713. {
  714. Win4Assert( !_fProcessingAPC );
  715. while ( 0 != _pDeferredAPCs )
  716. {
  717. //
  718. // Pull the item out of the list and invoke it.
  719. // DeferredAPC() is guaranteed not to throw.
  720. //
  721. PWorkItem * pItem = _pDeferredAPCs;
  722. _pDeferredAPCs = pItem->Next();
  723. pItem->Link( 0 );
  724. pItem->DeferredAPC();
  725. // Release once per APC processed
  726. Release();
  727. }
  728. } //ProcessDeferredAPCs
  729. //+-------------------------------------------------------------------------
  730. //
  731. // Member: CWorkThread::Wait, public
  732. //
  733. // Synopsis: Waits for 'new work' event
  734. //
  735. // History: 29-Dec-93 KyleP Created
  736. //
  737. //--------------------------------------------------------------------------
  738. void CWorkThread::Wait()
  739. {
  740. vqDebugOut(( DEB_RESULTS, "Worker 0x%x: WAIT for work\n", this ));
  741. // Process any deferred APCs before sleeping to get more.
  742. ProcessDeferredAPCs();
  743. //
  744. // There is no need to loop to look for work just because we woke
  745. // up after processing an APC. But do process deferred APCs.
  746. //
  747. while ( STATUS_USER_APC == _evtQueryAvailable.Wait( INFINITE, TRUE ) )
  748. ProcessDeferredAPCs();
  749. } //Wait
  750. //+-------------------------------------------------------------------------
  751. //
  752. // Member: CWorkThread::Done, public
  753. //
  754. // Effects: Finishes one work item and sets 'done' event.
  755. //
  756. // History: 29-Dec-93 KyleP Created
  757. //
  758. //--------------------------------------------------------------------------
  759. void CWorkThread::Done()
  760. {
  761. _dwLastUsed = GetTickCount();
  762. CLock lock( _queue._mtxLock );
  763. ActiveItem()->Release();
  764. lokSetActiveItem( 0 );
  765. vqDebugOut(( DEB_ITRACE, "Worker 0x%x: SET completion\n", this ));
  766. _evtDone.Set();
  767. }
  768. //+-------------------------------------------------------------------------
  769. //
  770. // Member: CWorkThread::WaitForCompletion, public
  771. //
  772. // Synopsis: Waits for completion of current work item.
  773. //
  774. // History: 29-Dec-93 KyleP Created
  775. //
  776. //--------------------------------------------------------------------------
  777. void CWorkThread::WaitForCompletion( PWorkItem * pitem )
  778. {
  779. {
  780. CLock lock( _queue._mtxLock );
  781. if ( ActiveItem() != pitem )
  782. return;
  783. vqDebugOut(( DEB_ITRACE, "Worker 0x%x: RESET completion\n", this ));
  784. _evtDone.Reset();
  785. }
  786. vqDebugOut(( DEB_ITRACE, "Worker 0x%x: WAIT for completion\n", this ));
  787. _evtDone.Wait( INFINITE, TRUE );
  788. } //WaitForCompletion
  789. //+-------------------------------------------------------------------------
  790. //
  791. // Member: CWorkThread::DeferAPC, public
  792. //
  793. // Synopsis: Puts the work item on a list to be called back later when
  794. // no APC is being processed.
  795. //
  796. // History: 17-Jul-00 dlee Created
  797. //
  798. //--------------------------------------------------------------------------
  799. void CWorkThread::DeferAPC( PWorkItem * pItem )
  800. {
  801. //
  802. // Make sure the thread doesn't go away until all APCs are processed.
  803. //
  804. Win4Assert( _Thread.GetThreadId() == GetCurrentThreadId() );
  805. AddRef();
  806. //
  807. // Add the item to the list.
  808. // No locking is needed since only this thread can call it.
  809. pItem->Link( _pDeferredAPCs );
  810. _pDeferredAPCs = pItem;
  811. } //DeferAPC
  812. //+-------------------------------------------------------------------------
  813. //
  814. // Member: CWorkThread::SetProcessingAPC, public
  815. //
  816. // Synopsis: Sets the flags that keep track of whether APC work is going
  817. // on.
  818. //
  819. // Arguments: [f] -- TRUE if processing an APC or FALSE if all done.
  820. //
  821. // History: 7-Jan-01 dlee Created
  822. //
  823. //--------------------------------------------------------------------------
  824. void CWorkThread::SetProcessingAPC( BOOL f )
  825. {
  826. _fProcessingAPC = f;
  827. if ( f )
  828. _queue.IncrementAPC();
  829. else
  830. _queue.DecrementAPC();
  831. } //SetProcessingAPC