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.

1074 lines
25 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1995 - 1999
  3. All rights reserved.
  4. Module Name:
  5. notify.cxx
  6. Abstract:
  7. Handles object updates and notifications from the printing system.
  8. Author:
  9. Albert Ting (AlbertT) 15-Sept-1994
  10. Revision History:
  11. Lazar Ivanov (LazarI) Nov-20-2000, major redesign
  12. --*/
  13. #include "precomp.hxx"
  14. #pragma hdrstop
  15. #include "notify.hxx"
  16. /********************************************************************
  17. Public functions.
  18. ********************************************************************/
  19. TNotify::
  20. TNotify(
  21. VOID
  22. )
  23. /*++
  24. Routine Description:
  25. Constructs a Notify object that can be used to watch several
  26. event handles.
  27. Arguments:
  28. Return Value:
  29. Notes:
  30. --*/
  31. {
  32. DBGMSG( DBG_NOTIFY, ( "Notify.Notify: ctr %x\n", this ));
  33. //
  34. // Initialize member fields.
  35. //
  36. CSGuard._bSuspended = FALSE;
  37. _dwSleepTime = kSleepTime;
  38. _shEventProcessed = CreateEvent(NULL, FALSE, FALSE, NULL);
  39. //
  40. // _shEventProcessed is our valid check.
  41. //
  42. }
  43. VOID
  44. TNotify::
  45. vDelete(
  46. VOID
  47. )
  48. /*++
  49. Routine Description:
  50. Mark the object as pending deletion.
  51. Arguments:
  52. Return Value:
  53. --*/
  54. {
  55. //
  56. // Notify all objects on linked list.
  57. //
  58. {
  59. CCSLock::Locker lock(_CritSec);
  60. CSGuard._pNotifyWork = NULL;
  61. TWait *pWait = NULL;
  62. for(;;)
  63. {
  64. pWait = CSGuard.Wait_pHead();
  65. if( !pWait )
  66. {
  67. break;
  68. }
  69. // we are about to shutdown -- no handles should be
  70. // registered at this time.
  71. SPLASSERT(0 == pWait->_cNotifyWork);
  72. // if our wait is not suspended then we need to suspend it first
  73. // to ensure only our request will be processed during this time
  74. // this will help us avoid deadlocks when WaitForMultipleObjects
  75. // fails for some reason (due to invalid handles for example) and
  76. // never get to the point of processing the exit request.
  77. if( !CSGuard._bSuspended )
  78. {
  79. CSGuard._eOperation = kEopSuspendRequest;
  80. vSendRequest(pWait);
  81. WaitForSingleObject(_shEventProcessed, INFINITE);
  82. }
  83. // TWait should be suspended at this point (i.e. waiting only for
  84. // commands). now just request exit...
  85. CSGuard._eOperation = kEopExitRequest;
  86. vSendRequest(pWait);
  87. WaitForSingleObject(_shEventProcessed, INFINITE);
  88. // Before destoying pWait we should wait the background
  89. // (worker) thread to finish.
  90. WaitForSingleObject(pWait->_shThread, INFINITE);
  91. // at this point the background thread is exiting and this pWait
  92. // object will never be accessed again, so it is safe to delete.
  93. pWait->Wait_vDelinkSelf();
  94. delete pWait;
  95. }
  96. }
  97. }
  98. VOID
  99. TNotify::
  100. vSendRequest(
  101. TWait* pWait
  102. )
  103. /*++
  104. Routine Description:
  105. Send a request to the pWait thread. The pWait thread will pickup
  106. our notification, process it, then shend a handshake.
  107. Arguments:
  108. Return Value:
  109. --*/
  110. {
  111. SetEvent( pWait->_ahNotifyArea[0] );
  112. }
  113. /*++
  114. Routine Description:
  115. executes an operation on a wait object
  116. we need to suspend the object first to
  117. ensure no deadlocking.
  118. Arguments:
  119. Return Value:
  120. --*/
  121. VOID
  122. TNotify::
  123. vExecuteOperation(
  124. MNotifyWork* pNotifyWork,
  125. TWait* pWait,
  126. EOPERATION eOp
  127. )
  128. {
  129. SPLASSERT(_CritSec.bInside());
  130. if( pWait && eOp )
  131. {
  132. //
  133. // The TWait will look in these globals to determine which
  134. // pNotifyWork it should register.
  135. //
  136. CSGuard._pNotifyWork = pNotifyWork;
  137. // if our wait is not suspended then we need to suspend it first
  138. // (temporarily) to ensure only our request will be processed during
  139. // this time this will also help us avoid deadlocks when WaitForMultipleObjects
  140. // fails for some reason (due to invalid handles for example) and
  141. // never get to the point of registering the object.
  142. if( !CSGuard._bSuspended )
  143. {
  144. CSGuard._eOperation = kEopSuspendRequest;
  145. vSendRequest(pWait);
  146. WaitForSingleObject(_shEventProcessed, INFINITE);
  147. }
  148. // the Wait obj should be suspended at this point and ready to
  149. // accept registration requests.
  150. //
  151. // We must perform a handshake to ensure that only one register
  152. // occurs at a time. We will sit in the critical section until
  153. // the worker signals us that they are done.
  154. //
  155. CSGuard._eOperation = eOp;
  156. vSendRequest(pWait);
  157. WaitForSingleObject(_shEventProcessed, INFINITE);
  158. // release the Wait from suspended mode after the registration
  159. // request is handled
  160. if( !CSGuard._bSuspended )
  161. {
  162. CSGuard._eOperation = kEopResumeRequest;
  163. vSendRequest(pWait);
  164. WaitForSingleObject(_shEventProcessed, INFINITE);
  165. }
  166. }
  167. }
  168. /*++
  169. Routine Description:
  170. finds a wait object which has a free slot.
  171. if not found - create new one
  172. Arguments:
  173. Return Value:
  174. --*/
  175. STATUS
  176. TNotify::
  177. sFindOrCreateWaitObject(
  178. TWait **ppWait
  179. )
  180. {
  181. SPLASSERT(_CritSec.bInside());
  182. SPLASSERT(ppWait);
  183. TWait* pWait = NULL;
  184. TStatus Status(DBG_WARN);
  185. Status DBGCHK = ERROR_SUCCESS;
  186. //
  187. // Traverse through all the TWaits
  188. // to find the first one that has free slot.
  189. //
  190. TIter Iter;
  191. for( CSGuard.Wait_vIterInit(Iter), Iter.vNext(); Iter.bValid(); Iter.vNext() )
  192. {
  193. pWait = CSGuard.Wait_pConvert(Iter);
  194. if( FALSE == pWait->bFull() )
  195. {
  196. // found a wait with an empty slot
  197. break;
  198. }
  199. }
  200. //
  201. // If the iter is valid, we broke out of the loop because
  202. // we found one. If it's not valid, we need to create
  203. // a new wait object.
  204. //
  205. if( !Iter.bValid( ))
  206. {
  207. //
  208. // We need to create a new wait object.
  209. //
  210. pWait = new TWait(this);
  211. if( !VALID_PTR(pWait) )
  212. {
  213. Status DBGCHK = GetLastError();
  214. SPLASSERT((STATUS)Status);
  215. delete pWait;
  216. pWait = NULL;
  217. }
  218. else
  219. {
  220. //
  221. // If we are in suspended mode then we need to suspend the new
  222. // TWait object before start registering the handles.
  223. //
  224. if( CSGuard._bSuspended )
  225. {
  226. CSGuard._eOperation = kEopSuspendRequest;
  227. vSendRequest(pWait);
  228. WaitForSingleObject(_shEventProcessed, INFINITE);
  229. }
  230. }
  231. }
  232. *ppWait = pWait;
  233. return Status;
  234. }
  235. STATUS
  236. TNotify::
  237. sRegister(
  238. MNotifyWork* pNotifyWork
  239. )
  240. /*++
  241. Routine Description:
  242. Registers a notification work item with the system. It may
  243. already be watched; in this case, the notification is modified
  244. (this may occur if the event handle needs to change).
  245. Arguments:
  246. pNotifyWork - Item that should be modified.
  247. Return Value:
  248. STATUS - ERROR_SUCCESS = success, else error.
  249. --*/
  250. {
  251. TStatus Status(DBG_WARN);
  252. Status DBGCHK = ERROR_SUCCESS;
  253. {
  254. CCSLock::Locker lock(_CritSec);
  255. //
  256. // If registering, make sure the event is OK.
  257. //
  258. if( pNotifyWork->hEvent() == INVALID_HANDLE_VALUE || !pNotifyWork->hEvent() )
  259. {
  260. SPLASSERT( FALSE );
  261. return ERROR_INVALID_PARAMETER;
  262. }
  263. if( NULL == pNotifyWork->_pWait )
  264. {
  265. //
  266. // This is a new item requesting to be registered.
  267. // find a free slot in the TWait object.
  268. //
  269. TWait* pWait = NULL;
  270. Status DBGCHK = sFindOrCreateWaitObject(&pWait);
  271. if( ERROR_SUCCESS == Status && pWait )
  272. {
  273. //
  274. // If success then register the handle
  275. //
  276. vExecuteOperation(pNotifyWork, pWait, kEopRegister);
  277. //
  278. // Hook up the wait object
  279. //
  280. pNotifyWork->_pWait = pWait;
  281. }
  282. }
  283. else
  284. {
  285. //
  286. // We are already hooked up on on a wait object,
  287. // just modify the handle if necessary
  288. //
  289. vExecuteOperation(pNotifyWork, pNotifyWork->_pWait, kEopModify);
  290. }
  291. }
  292. return Status;
  293. }
  294. STATUS
  295. TNotify::
  296. sUnregister(
  297. MNotifyWork* pNotifyWork
  298. )
  299. /*++
  300. Routine Description:
  301. Unregister a work item to be watched by the system.
  302. Arguments:
  303. pNotifyWork - Item that should be modified.
  304. Return Value:
  305. STATUS - ERROR_SUCCESS = success, else error.
  306. --*/
  307. {
  308. {
  309. CCSLock::Locker lock(_CritSec);
  310. //
  311. // Only do this if the TNotifyWork is registered.
  312. //
  313. if( pNotifyWork->_pWait )
  314. {
  315. vExecuteOperation(pNotifyWork, pNotifyWork->_pWait, kEopUnregister);
  316. //
  317. // We are now unregistered, remove _pWait.
  318. //
  319. pNotifyWork->_pWait = NULL;
  320. }
  321. else
  322. {
  323. //
  324. // Deleting handle, but it doesn't exist.
  325. //
  326. DBGMSG(DBG_NOTIFY, ("Notify.sUnregister: %x Del NotifyWork %x not on list\n",
  327. this, pNotifyWork));
  328. }
  329. }
  330. return ERROR_SUCCESS;
  331. }
  332. BOOL
  333. TNotify::
  334. bSuspendCallbacks(
  335. VOID
  336. )
  337. /*++
  338. Routine Description:
  339. This function is suspending temporarily the notify callbacks
  340. from the background threads, which are listening for a printer
  341. notifications and force them to enter in special mode for
  342. listening register/unregister requests only. We need
  343. this functionality in order to avoid deadlocks caused between
  344. the TFolder::bFolderRefresh() and the callback functions -
  345. vProcessNotifyWork() invoked from the background threads both
  346. of which are trying to grab the TFolder critical section.
  347. Arguments:
  348. None
  349. Return Value:
  350. TRUE - callbacks suspended
  351. FALSE - otherwise
  352. --*/
  353. {
  354. CCSLock::Locker lock(_CritSec);
  355. SPLASSERT(_csResumeSuspend.bInside());
  356. SPLASSERT(!CSGuard._bSuspended);
  357. CSGuard._eOperation = kEopSuspendRequest;
  358. TIter Iter;
  359. TWait* pWait = NULL;
  360. for( CSGuard.Wait_vIterInit( Iter ), Iter.vNext(); Iter.bValid(); Iter.vNext() )
  361. {
  362. pWait = CSGuard.Wait_pConvert(Iter);
  363. vSendRequest(pWait);
  364. WaitForSingleObject(_shEventProcessed, INFINITE);
  365. }
  366. CSGuard._bSuspended = TRUE;
  367. return TRUE;
  368. }
  369. VOID
  370. TNotify::
  371. vResumeCallbacks(
  372. VOID
  373. )
  374. /*++
  375. Routine Description:
  376. Resume the callbacks suspended from bSuspendCallbacks(). For more
  377. information see bSuspendCallbacks's description.
  378. Arguments:
  379. None
  380. Return Value:
  381. TRUE - callbacks suspended
  382. FALSE - otherwise
  383. --*/
  384. {
  385. CCSLock::Locker lock(_CritSec);
  386. SPLASSERT(_csResumeSuspend.bInside());
  387. SPLASSERT(CSGuard._bSuspended);
  388. CSGuard._eOperation = kEopResumeRequest;
  389. TIter Iter;
  390. TWait* pWait = NULL;
  391. for( CSGuard.Wait_vIterInit(Iter), Iter.vNext(); Iter.bValid(); Iter.vNext() )
  392. {
  393. pWait = CSGuard.Wait_pConvert(Iter);
  394. vSendRequest(pWait);
  395. WaitForSingleObject(_shEventProcessed, INFINITE);
  396. }
  397. CSGuard._bSuspended = FALSE;
  398. }
  399. /********************************************************************
  400. Private functions
  401. ********************************************************************/
  402. VOID
  403. TNotify::
  404. vRefZeroed(
  405. VOID
  406. )
  407. /*++
  408. Routine Description:
  409. Virtual definition for MRefCom. When the refcount has reached
  410. zero, we want to delete ourselves.
  411. Arguments:
  412. Return Value:
  413. --*/
  414. {
  415. if( bValid( )){
  416. delete this;
  417. }
  418. }
  419. TNotify::
  420. ~TNotify(
  421. VOID
  422. )
  423. /*++
  424. Routine Description:
  425. Delete TNotify.
  426. Arguments:
  427. Return Value:
  428. --*/
  429. {
  430. DBGMSG(DBG_NOTIFY, ("Notify.Notify: dtr %x\n", this));
  431. SPLASSERT(CSGuard.Wait_bEmpty());
  432. }
  433. /********************************************************************
  434. TWait
  435. ********************************************************************/
  436. TNotify::
  437. TWait::
  438. TWait(
  439. TNotify* pNotify
  440. ) :
  441. _pNotify(pNotify),
  442. _cNotifyWork(0)
  443. /*++
  444. Routine Description:
  445. Construct TWait.
  446. TWaits are built when new object need to be watched. We
  447. need several TWaits since only 31 handles can be watched
  448. per TWait.
  449. Arguments:
  450. pNotify - Owning TNotify.
  451. Return Value:
  452. --*/
  453. {
  454. SPLASSERT( pNotify->_CritSec.bInside( ));
  455. SPLASSERT( pNotify );
  456. DWORD dwIgnore;
  457. //
  458. // Create our trigger event to add and remove events.
  459. //
  460. _ahNotifyArea[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
  461. //
  462. // _ahNotifyArea[0] is our valid check.
  463. //
  464. if( !_ahNotifyArea[0] )
  465. {
  466. return;
  467. }
  468. _shThread = TSafeThread::Create(NULL,
  469. 0,
  470. (LPTHREAD_START_ROUTINE)vRun,
  471. this,
  472. 0,
  473. &dwIgnore);
  474. if( !_shThread )
  475. {
  476. goto Fail;
  477. }
  478. //
  479. // Add ourselves to the linked list.
  480. //
  481. pNotify->CSGuard.Wait_vAdd(this);
  482. return;
  483. Fail:
  484. //
  485. // _ahNotifyArea[0] is our valid check, so clear it here.
  486. //
  487. CloseHandle(_ahNotifyArea[0]);
  488. _ahNotifyArea[0] = NULL;
  489. }
  490. TNotify::
  491. TWait::
  492. ~TWait(
  493. VOID
  494. )
  495. /*++
  496. Routine Description:
  497. Destory TWait.
  498. Should be called once everything has been unregistered.
  499. Arguments:
  500. Return Value:
  501. --*/
  502. {
  503. if( _ahNotifyArea[0] )
  504. {
  505. CloseHandle(_ahNotifyArea[0]);
  506. }
  507. }
  508. VOID
  509. TNotify::
  510. TWait::
  511. vProcessOperation(
  512. VOID
  513. )
  514. /*++
  515. Routine Description:
  516. Process registration/unregistration of handles.
  517. Note: doesn't reset cAwake, so callee must reset.
  518. This routine MUST be called when someone else is holding the
  519. critical section, since we access data here. This routine will
  520. be called when another thread needs to register/unregister, so
  521. it grabs the critical section, sets the correct state, then sets
  522. the event which calls us. When we are done, we set another event
  523. and the calling thread releases the critical section.
  524. This routine doesn't not move items across the cAwake boundary, so
  525. the callee must reset and watch all events (e.g., we may unregister
  526. an event that was awake, and we replace it with one that wasn't).
  527. Arguments:
  528. None.
  529. Return Value:
  530. --*/
  531. {
  532. MNotifyWork* pNotifyWork = pNotify()->CSGuard._pNotifyWork;
  533. //
  534. // Switch based on operation.
  535. //
  536. switch( pNotify()->CSGuard._eOperation )
  537. {
  538. case kEopRegister:
  539. //
  540. // Add it to our list.
  541. //
  542. SPLASSERT(_cNotifyWork < kNotifyWorkMax);
  543. SPLASSERT(NULL == pNotifyWork->_pWait);
  544. if( _cNotifyWork < kNotifyWorkMax )
  545. {
  546. DBGMSG( DBG_NOTIFY,
  547. ( "Wait.vProcessOperation: %x Register %x (%d)\n",
  548. pNotify(), this, _cNotifyWork ));
  549. //
  550. // Add to the list by putting it at the end.
  551. //
  552. phNotifys()[_cNotifyWork] = pNotifyWork->hEvent();
  553. _apNotifyWork[_cNotifyWork] = pNotifyWork;
  554. ++_cNotifyWork;
  555. }
  556. break;
  557. case kEopModify:
  558. case kEopUnregister:
  559. UINT i = 0;
  560. //
  561. // Find the index of the MNotifyWork in TWait.
  562. //
  563. for( i = 0; i< _cNotifyWork; ++i )
  564. {
  565. if( _apNotifyWork[i] == pNotifyWork )
  566. {
  567. break;
  568. }
  569. }
  570. DBGMSG( DBG_NOTIFY,
  571. ( "Wait.bNotify: %x update hEvent on index %d %x (%d)\n",
  572. this, i, phNotifys()[i], _cNotifyWork ));
  573. SPLASSERT( i != _cNotifyWork );
  574. if( i < _cNotifyWork )
  575. {
  576. if( pNotify()->CSGuard._eOperation == kEopModify )
  577. {
  578. //
  579. // Update hEvent only. This is necessary because the client
  580. // may have very quickly deleted and re-added with a new
  581. // event. In this case, we must do the update.
  582. //
  583. phNotifys()[i] = pNotifyWork->hEvent();
  584. }
  585. else
  586. {
  587. //
  588. // We are going to reset after this anyway, so don't
  589. // worry about moving items across the cAwake
  590. // boundary.
  591. //
  592. //
  593. // Fill in the last element into the hole.
  594. //
  595. DBGMSG( DBG_NOTIFY,
  596. ( "Wait.vProcessOperation: %x removing index %d %x (%d)\n",
  597. this, i, phNotifys()[i], _cNotifyWork ));
  598. //
  599. // Predecrement since array is zero-based.
  600. //
  601. --_cNotifyWork;
  602. phNotifys()[i] = phNotifys()[_cNotifyWork];
  603. _apNotifyWork[i] = _apNotifyWork[_cNotifyWork];
  604. }
  605. }
  606. break;
  607. }
  608. }
  609. VOID
  610. TNotify::
  611. TWait::
  612. vRun(
  613. TWait* pWait
  614. )
  615. {
  616. DWORD dwWait;
  617. DWORD cObjects;
  618. DWORD cAwake;
  619. DWORD dwTimeout = INFINITE;
  620. MNotifyWork* pNotifyWork;
  621. TNotify* pNotify = pWait->pNotify().pGet();
  622. DBGMSG( DBG_NOTIFY, ( "Wait.vRun start %x\n", pWait ));
  623. Reset:
  624. cAwake =
  625. cObjects = pWait->_cNotifyWork;
  626. dwTimeout = INFINITE;
  627. for( ; ; )
  628. {
  629. DBGMSG( DBG_NOTIFY,
  630. ( "WaitForMultpleObjects: 1+%d %x timeout %d cObjects %d\n"
  631. "%x %x %x %x %x %x %x %x %x\n"
  632. "%x %x %x %x %x %x %x %x %x\n",
  633. cAwake,
  634. pNotify,
  635. dwTimeout,
  636. cObjects,
  637. pWait->_apNotifyWork[0],
  638. pWait->_apNotifyWork[1],
  639. pWait->_apNotifyWork[2],
  640. pWait->_apNotifyWork[3],
  641. pWait->_apNotifyWork[4],
  642. pWait->_apNotifyWork[5],
  643. pWait->_apNotifyWork[6],
  644. pWait->_apNotifyWork[7],
  645. pWait->_apNotifyWork[8],
  646. pWait->phNotifys()[0],
  647. pWait->phNotifys()[1],
  648. pWait->phNotifys()[2],
  649. pWait->phNotifys()[3],
  650. pWait->phNotifys()[4],
  651. pWait->phNotifys()[5],
  652. pWait->phNotifys()[6],
  653. pWait->phNotifys()[7],
  654. pWait->phNotifys()[8] ));
  655. //
  656. // Wait on multiple NotifyWork notification handles.
  657. // (Including our trigger event.)
  658. //
  659. dwWait = WaitForMultipleObjects(
  660. cAwake + 1,
  661. pWait->_ahNotifyArea,
  662. FALSE,
  663. dwTimeout);
  664. if( dwWait == WAIT_FAILED )
  665. {
  666. // that means we ended up with WAIT_FAILED, this should
  667. // never happen, so just break if we are running under a debugger
  668. RIP(false);
  669. // handle this in the case where we are not under a debugger
  670. Sleep( pNotify->_dwSleepTime );
  671. goto Reset;
  672. }
  673. //
  674. // This is a liitle noise, which help us to repro the
  675. // race condition of bug #321606.
  676. //
  677. // if( rand() % 2 )
  678. // Sleep( 4 + rand() % 20 );
  679. if( dwWait == WAIT_TIMEOUT )
  680. {
  681. DBGMSG( DBG_NOTIFY,
  682. ( "Notify.vRun: %x TIMEOUT, cAwake old %d cObjects %d (%d)\n",
  683. pNotify, cAwake, cObjects, pWait->_cNotifyWork ));
  684. //
  685. // We had a notification recently, but we didn't want to spin,
  686. // so we took it out of the list. Add everyone back in.
  687. //
  688. goto Reset;
  689. }
  690. if( dwWait == WAIT_OBJECT_0 )
  691. {
  692. //
  693. // We are not suspended now, so resume requests shouldn't occur here.
  694. //
  695. SPLASSERT(pNotify->CSGuard._eOperation != TNotify::kEopResumeRequest);
  696. if( pNotify->CSGuard._eOperation == TNotify::kEopSuspendRequest )
  697. {
  698. //
  699. // Someone has requested us to suspend the callbacks and
  700. // and wait on the trigger event for the register/unregister
  701. // requests only.
  702. //
  703. SetEvent(pNotify->_shEventProcessed);
  704. for( ;; )
  705. {
  706. if( WAIT_OBJECT_0 == WaitForSingleObject(pWait->_ahNotifyArea[0], INFINITE) )
  707. {
  708. //
  709. // We are in suspended mode already, so suspend requests shouldn't occur here.
  710. //
  711. SPLASSERT(pNotify->CSGuard._eOperation != TNotify::kEopSuspendRequest);
  712. if( pNotify->CSGuard._eOperation == TNotify::kEopExitRequest )
  713. {
  714. //
  715. // We've been asked to exit the thead. This should only happen
  716. // when there are no more handles to wait on.
  717. //
  718. SPLASSERT( !pWait->_cNotifyWork );
  719. DBGMSG( DBG_NOTIFY, ( "Wait.vRun exits %x\n", pWait ));
  720. SetEvent(pNotify->_shEventProcessed);
  721. return; // exit thread!
  722. }
  723. if( pNotify->CSGuard._eOperation == TNotify::kEopResumeRequest )
  724. {
  725. //
  726. // Resume watching all event handles.
  727. //
  728. SetEvent(pNotify->_shEventProcessed);
  729. goto Reset;
  730. }
  731. pWait->vProcessOperation();
  732. SetEvent(pNotify->_shEventProcessed);
  733. }
  734. else
  735. {
  736. DBGMSG( DBG_ERROR,
  737. ( "Notify.Run: WaitForSingleObject failed %x %d\n",
  738. pNotify, GetLastError( )));
  739. goto Reset;
  740. }
  741. }
  742. }
  743. //
  744. // We have a genuine register event. The thread that triggered
  745. // this event holds the critical section, so we don't need to
  746. // grab it. When we set _shEventProcessed, the requester will
  747. // release it.
  748. //
  749. pWait->vProcessOperation();
  750. SetEvent(pNotify->_shEventProcessed);
  751. DBGMSG( DBG_NOTIFY,
  752. ( "Notify.vRun: %x ProcessOperation, cAwake old %d cObjects %d (%d)\n",
  753. pNotify, cAwake, cObjects, pWait->_cNotifyWork ));
  754. goto Reset;
  755. }
  756. if( dwWait < WAIT_OBJECT_0 + cObjects + 1 )
  757. {
  758. dwWait -= WAIT_OBJECT_0;
  759. SPLASSERT( dwWait != 0 );
  760. //
  761. // dwWait is one over the number of the item that triggered
  762. // since we put our own handle in slot 0.
  763. //
  764. --dwWait;
  765. //
  766. // NotifyWork has notification, process it.
  767. //
  768. pWait->_apNotifyWork[dwWait]->vProcessNotifyWork(pNotify);
  769. //
  770. // Once we handle a notification on a NotifyWork, we
  771. // don't want to watch the handle immediately again,
  772. // since we may spin in a tight loop getting notifications.
  773. // Instead, ignore the handle and sleep for a bit.
  774. //
  775. if( dwTimeout == INFINITE )
  776. {
  777. dwTimeout = pNotify->_dwSleepTime;
  778. }
  779. DBGMSG( DBG_NOTIFY,
  780. ( "Wait.vRun: %x index going to sleep %d work %x handle %x (%d)\n",
  781. pWait, dwWait,
  782. pWait->_apNotifyWork[dwWait],
  783. pWait->phNotifys()[dwWait],
  784. pWait->_cNotifyWork ));
  785. //
  786. // Swap it to the end so that we can decrement
  787. // cObjects and not watch it for a while.
  788. //
  789. HANDLE hNotify = pWait->phNotifys()[dwWait];
  790. pNotifyWork = pWait->_apNotifyWork[dwWait];
  791. //
  792. // Another one has gone to sleep. Decrement it now.
  793. //
  794. --cAwake;
  795. //
  796. // Now swap the element that needs to sleep with the
  797. // element that's at the end of the list.
  798. //
  799. pWait->phNotifys()[dwWait] = pWait->phNotifys()[cAwake];
  800. pWait->_apNotifyWork[dwWait] = pWait->_apNotifyWork[cAwake];
  801. pWait->phNotifys()[cAwake] = hNotify;
  802. pWait->_apNotifyWork[cAwake] = pNotifyWork;
  803. }
  804. else
  805. {
  806. DBGMSG( DBG_ERROR,
  807. ( "Notify.Run: WaitForMultipleObjects failed %x %d\n",
  808. pNotify, GetLastError( )));
  809. // that means we ended up with WAIT_ABANDONED + N, this should
  810. // never happen, so just break if we are running under a debugger
  811. RIP(false);
  812. Sleep( pNotify->_dwSleepTime );
  813. goto Reset;
  814. }
  815. }
  816. }
  817. /////////////////////////////////////////////////////
  818. // MNotifyWork
  819. //
  820. MNotifyWork::
  821. ~MNotifyWork(
  822. VOID
  823. )
  824. {
  825. // must be unregistered
  826. RIP(NULL == _pWait);
  827. }