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.

1797 lines
50 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. wait.c
  5. Abstract:
  6. This module defines functions for the wait thread pool.
  7. Author:
  8. Gurdeep Singh Pall (gurdeep) Nov 13, 1997
  9. Revision History:
  10. lokeshs - extended/modified threadpool.
  11. Rob Earhart (earhart) September 29, 2000
  12. Split off from threads.c
  13. Environment:
  14. These routines are statically linked in the caller's executable
  15. and are callable only from user mode. They make use of Nt system
  16. services.
  17. --*/
  18. #include <ntos.h>
  19. #include <ntrtl.h>
  20. #include "ntrtlp.h"
  21. #include "threads.h"
  22. // Wait Thread Pool
  23. // ----------------
  24. // Clients can submit a waitable object with an optional timeout to wait on.
  25. // One thread is created per MAXIMUM_WAIT_OBJECTS-1 such waitable objects.
  26. //
  27. // Lifecycle of a wait object:
  28. //
  29. // Things start when a client calls RtlRegisterWait. RtlRegisterWait
  30. // allocates memory for the wait object, captures the activation
  31. // context, calls RtlpFindWaitThread to obtain a wait thread, and
  32. // queues an RtlpAddWait APC to the wait thread. The wait is now
  33. // bound to this wait thread for the rest of its life cycle.
  34. //
  35. // RtlpAddWait executes in an APC in the wait thread. If the wait's
  36. // deleted bit has already been set, it simply deletes the wait;
  37. // otherwise, it adds the wait to the thread's wait array block, and
  38. // sets the wait's STATE_REGISTERED and STATE_ACTIVE flags. It also
  39. // creates a timer object if necessary, and sets the wait's refcount
  40. // to one.
  41. //
  42. // RtlDeregisterWait is a wrapper for RtlDeregisterWaitEx.
  43. // RtlDeregisterWaitEx gets a completion event if necessary, and
  44. // stuffs it or the user's supplied event into the wait. If
  45. // RtlDeregisterWaitEx is called within the wait thread, it then calls
  46. // RtlpDeregisterWait directly; otherwise, it allocates a partial
  47. // completion event, and queues RtlpDeregisterWait as an APC to the
  48. // wait's thread, and blocks until the partial completion event is
  49. // triggered (indicating that the APC in the wait thread has begun --
  50. // this means no more callbacks will occur). In a blocking call to
  51. // RtlDeregisterWaitEx, the function waits on the completion event,
  52. // and returns.
  53. //
  54. // RtlpDeregisterWait is always executed in the wait thread associated
  55. // with the wait it's being called on. It checks the state; if the
  56. // event hasn't been registered, then it's being called before
  57. // RtlpAddWait, so it sets the Deleted bit and returns -- RtlpAddWait
  58. // will delete the object when it sees this. Otherwise, if the wait
  59. // is active, it calls RtlpDeactivateWait (which calls
  60. // RtlpDeactivateWithIndex), sets the delete bit, decrements the
  61. // refcount, and if it has the last reference, calls RtlpDeleteWait to
  62. // reclaim the wait's memory. Finally, it sets the partial completion
  63. // event, if one was passed in.
  64. //
  65. // RtlpDeleteWait assumes that the last reference to the wait has gone
  66. // away; it sets the wait's completion event, if it has one, releases
  67. // the activation context, and frees the wait's memory.
  68. //
  69. // The wait thread runs RtlpWaitThread, which allocates a wait thread
  70. // block from its stack, initializes it with a timer and a handle on
  71. // the thread (among other things), tells its starter to proceed, and
  72. // drops into its wait loop. The thread waits on its objects,
  73. // restarting if alerted or if an APC is delivered (since the APC may
  74. // have adjusted the wait array). If the wait on the timer object
  75. // completes, the thread processes timeouts via RtlpProcessTimeouts;
  76. // otherwise, it calls RtlpProcessWaitCompletion to handle the
  77. // completed wait.
  78. //
  79. // If RtlpWaitThread finds an abandoned mutant or a bad handle, it
  80. // calls RtlpDeactivateWaitWithIndex to kill the wait.
  81. //
  82. // RtlpDeactivateWithIndex resets the wait's timer, turns off its
  83. // active bit, and removes it from the wait thread's wait array.
  84. //
  85. // RtlpProcessWaitCompletion deactivates the wait if it's meant to
  86. // only be executed once; otherwise, it resets the wait's timer, and
  87. // moves the wait to the end of the wait array. If the wait's
  88. // supposed to be executed in the wait thread, it calls the wait's
  89. // callback function directly; otherwise, it bumps up the wait's
  90. // refcount, and queues an RtlpAsyncWaitCallbackCompletion APC to a
  91. // worker thread.
  92. //
  93. // RtlpAsyncWaitCallbackCompletion makes the callout, and decrements
  94. // the refcount, calling RtlpDeleteWait if the refcount falls to
  95. // zero.
  96. //
  97. // RtlpProcessTimeouts calls RtlpFireTimersAndReorder to extract the
  98. // times to be fired, and then calls RtlpFireTimers to fire them.
  99. // This either calls the callback directly, or queues an
  100. // RtlpAsyncWaitCallbackCompletion APC to a worker thread (bumping up
  101. // the refcount in the process).
  102. //
  103. LIST_ENTRY WaitThreads ; // List of all wait threads created
  104. RTL_CRITICAL_SECTION WaitCriticalSection ; // Exclusion used by wait threads
  105. ULONG StartedWaitInitialization ; // Used for Wait thread startup synchronization
  106. ULONG CompletedWaitInitialization ; // Used to check if Wait thread pool is initialized
  107. HANDLE WaitThreadStartedEvent; // Indicates that a wait thread has started
  108. HANDLE WaitThreadTimer; // Timer handle for the new wait thread
  109. HANDLE WaitThreadHandle; // Thread handle for the new wait thread
  110. #if DBG1
  111. ULONG NextWaitDbgId;
  112. #endif
  113. NTSTATUS
  114. RtlpInitializeWaitThreadPool (
  115. )
  116. /*++
  117. Routine Description:
  118. This routine initializes all aspects of the thread pool.
  119. Arguments:
  120. None
  121. Return Value:
  122. None
  123. --*/
  124. {
  125. NTSTATUS Status = STATUS_SUCCESS;
  126. LARGE_INTEGER TimeOut ;
  127. // In order to avoid an explicit RtlInitialize() function to initialize the wait thread pool
  128. // we use StartedWaitInitialization and CompletedWait Initialization to provide us the
  129. // necessary synchronization to avoid multiple threads from initializing the thread pool.
  130. // This scheme does not work if RtlInitializeCriticalSection() or NtCreateEvent fails - but in this case the
  131. // caller has not choices left.
  132. if (!InterlockedExchange(&StartedWaitInitialization, 1L)) {
  133. if (CompletedWaitInitialization)
  134. InterlockedExchange (&CompletedWaitInitialization, 0L) ;
  135. // Initialize Critical Section
  136. Status = RtlInitializeCriticalSection( &WaitCriticalSection ) ;
  137. if (! NT_SUCCESS( Status ) ) {
  138. StartedWaitInitialization = 0 ;
  139. InterlockedExchange (&CompletedWaitInitialization, ~0) ;
  140. return Status ;
  141. }
  142. InitializeListHead (&WaitThreads); // Initialize global wait threads list
  143. InterlockedExchange (&CompletedWaitInitialization, 1L) ;
  144. } else {
  145. // Sleep 1 ms and see if the other thread has completed initialization
  146. ONE_MILLISECOND_TIMEOUT(TimeOut) ;
  147. while (!*((ULONG volatile *)&CompletedWaitInitialization)) {
  148. NtDelayExecution (FALSE, &TimeOut) ;
  149. }
  150. if (CompletedWaitInitialization != 1) {
  151. Status = STATUS_NO_MEMORY ;
  152. }
  153. }
  154. return Status ;
  155. }
  156. #if DBG
  157. VOID
  158. RtlpDumpWaits(
  159. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB
  160. )
  161. {
  162. ULONG Lupe;
  163. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  164. RTLP_THREADPOOL_VERBOSE_MASK,
  165. "Current active waits [Handle, Wait] for thread %x:",
  166. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread));
  167. for (Lupe = 0; Lupe < ThreadCB->NumActiveWaits; Lupe++) {
  168. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  169. RTLP_THREADPOOL_VERBOSE_MASK,
  170. "%s [%p, %p]",
  171. Lupe % 4 ? "" : "\n",
  172. ThreadCB->ActiveWaitArray[Lupe],
  173. ThreadCB->ActiveWaitPointers[Lupe]);
  174. }
  175. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  176. RTLP_THREADPOOL_VERBOSE_MASK,
  177. "\n%d (0x%x) total waits\n",
  178. ThreadCB->NumActiveWaits,
  179. ThreadCB->NumActiveWaits);
  180. }
  181. #endif
  182. VOID
  183. RtlpAddWait (
  184. PRTLP_WAIT Wait,
  185. PRTLP_GENERIC_TIMER Timer,
  186. PVOID ApcArgument3
  187. )
  188. /*++
  189. Routine Description:
  190. This routine is used for adding waits to the wait thread. It is executed in
  191. an APC.
  192. Arguments:
  193. Wait - The wait to add
  194. Timer - Space for the wait timer's memory (if needed)
  195. ApcArgument3 - Unused
  196. Return Value:
  197. --*/
  198. {
  199. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = Wait->ThreadCB;
  200. UNREFERENCED_PARAMETER(ApcArgument3);
  201. // if the state is deleted, it implies that RtlDeregister was called in a
  202. // WaitThreadCallback for a Wait other than that which was fired. This is
  203. // an application bug, but we handle it.
  204. if ( Wait->State & STATE_DELETE ) {
  205. InterlockedDecrement( &ThreadCB->NumWaits );
  206. RtlpDeleteWait (Wait);
  207. if (Timer) {
  208. RtlpFreeTPHeap(Timer);
  209. }
  210. return ;
  211. }
  212. // activate Wait
  213. ThreadCB->ActiveWaitArray [ThreadCB->NumActiveWaits] = Wait->WaitHandle ;
  214. ThreadCB->ActiveWaitPointers[ThreadCB->NumActiveWaits] = Wait ;
  215. #if DBG
  216. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  217. RTLP_THREADPOOL_TRACE_MASK,
  218. "<%d> Wait %p (Handle %p) inserted as index %d in thread %x\n",
  219. Wait->DbgId,
  220. Wait,
  221. Wait->WaitHandle,
  222. ThreadCB->NumActiveWaits,
  223. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread)) ;
  224. #endif
  225. ThreadCB->NumActiveWaits ++ ;
  226. #if DBG
  227. RtlpDumpWaits(ThreadCB);
  228. #endif
  229. ThreadCB->NumRegisteredWaits++;
  230. RtlInterlockedSetBitsDiscardReturn(&Wait->State,
  231. STATE_REGISTERED
  232. | STATE_ACTIVE);
  233. Wait->RefCount = 1 ;
  234. // Fill in the wait timer
  235. if (Wait->Timeout != INFINITE_TIME) {
  236. ULONG TimeRemaining ;
  237. ULONG NewFiringTime ;
  238. // Initialize timer related fields and insert the timer in the timer queue for
  239. // this wait thread
  240. ASSERT(Timer != NULL);
  241. Wait->Timer = Timer;
  242. Wait->Timer->Function = Wait->Function ;
  243. Wait->Timer->Context = Wait->Context ;
  244. Wait->Timer->Flags = Wait->Flags ;
  245. Wait->Timer->DeltaFiringTime = Wait->Timeout ;
  246. Wait->Timer->Period = ( Wait->Flags & WT_EXECUTEONLYONCE )
  247. ? 0
  248. : Wait->Timeout == INFINITE_TIME
  249. ? 0 : Wait->Timeout ;
  250. RtlInterlockedSetBitsDiscardReturn(&Wait->Timer->State,
  251. STATE_REGISTERED | STATE_ACTIVE);
  252. Wait->Timer->Wait = Wait ;
  253. Wait->Timer->RefCountPtr = &Wait->RefCount ;
  254. Wait->Timer->Queue = &ThreadCB->TimerQueue ;
  255. TimeRemaining = RtlpGetTimeRemaining (ThreadCB->TimerHandle) ;
  256. if (RtlpInsertInDeltaList (&ThreadCB->TimerQueue.TimerList, Wait->Timer,
  257. TimeRemaining, &NewFiringTime))
  258. {
  259. // If the element was inserted at head of list then reset the timers
  260. RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ;
  261. }
  262. } else {
  263. // No timer with this wait
  264. ASSERT(Timer == NULL);
  265. }
  266. return ;
  267. }
  268. VOID
  269. RtlpAsyncWaitCallbackCompletion(
  270. PVOID Context
  271. )
  272. /*++
  273. Routine Description:
  274. This routine is called in a (IO)worker thread and is used to decrement the
  275. RefCount at the end and call RtlpDeleteWait if required
  276. Arguments:
  277. Context - pointer to the Wait object
  278. Low bit of context: TimedOut parameter to RtlpWaitOrTimerCallout
  279. Return Value:
  280. --*/
  281. {
  282. BOOLEAN TimedOut = (BOOLEAN)((ULONG_PTR)Context & 1);
  283. PRTLP_WAIT Wait = (PRTLP_WAIT)((ULONG_PTR)Context & ~((ULONG_PTR) 1));
  284. #if DBG
  285. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  286. RTLP_THREADPOOL_TRACE_MASK,
  287. "<%d> Calling Wait %p: Handle:%p fn:%p context:%p bool:%d Thread<%x:%x>\n",
  288. Wait->DbgId,
  289. Wait,
  290. Wait->WaitHandle,
  291. Wait->Function,
  292. Wait->Context,
  293. (ULONG)TimedOut,
  294. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  295. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)
  296. ) ;
  297. #endif
  298. RtlpWaitOrTimerCallout(Wait->Function,
  299. Wait->Context,
  300. TimedOut,
  301. Wait->ActivationContext,
  302. Wait->ImpersonationToken);
  303. if ( InterlockedDecrement( &Wait->RefCount ) == 0 ) {
  304. RtlpDeleteWait( Wait ) ;
  305. }
  306. }
  307. NTSTATUS
  308. RtlpDeactivateWaitWithIndex (
  309. PRTLP_WAIT Wait,
  310. BOOLEAN OkayToFreeTheTimer,
  311. ULONG ArrayIndex
  312. )
  313. /*++
  314. Routine Description:
  315. This routine is used for deactivating the specified wait. It is executed in a APC.
  316. Arguments:
  317. Wait - The wait to deactivate
  318. OkayToFreeTheTimer - If TRUE, we can delete the wait's timer immediately;
  319. otherwise, we need to keep it around.
  320. ArrayIndex - The index of the wait to deactivate
  321. Return Value:
  322. --*/
  323. {
  324. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = Wait->ThreadCB ;
  325. ULONG EndIndex = ThreadCB->NumActiveWaits -1;
  326. ASSERT(Wait == ThreadCB->ActiveWaitPointers[ArrayIndex]);
  327. #if DBG
  328. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  329. RTLP_THREADPOOL_TRACE_MASK,
  330. "<%d> Deactivating Wait %p (Handle %p); index %d in thread %x\n",
  331. Wait->DbgId,
  332. Wait,
  333. Wait->WaitHandle,
  334. ArrayIndex,
  335. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread)) ;
  336. #endif
  337. // Move the remaining ActiveWaitArray left.
  338. RtlpShiftWaitArray( ThreadCB, ArrayIndex+1, ArrayIndex,
  339. EndIndex - ArrayIndex ) ;
  340. //
  341. // delete timer if associated with this wait
  342. //
  343. if ( Wait->Timer ) {
  344. ULONG TimeRemaining ;
  345. ULONG NewFiringTime ;
  346. if (! (Wait->Timer->State & STATE_ACTIVE) ) {
  347. RemoveEntryList( &Wait->Timer->List ) ;
  348. } else {
  349. TimeRemaining = RtlpGetTimeRemaining (ThreadCB->TimerHandle) ;
  350. if (RtlpRemoveFromDeltaList (&ThreadCB->TimerQueue.TimerList, Wait->Timer,
  351. TimeRemaining, &NewFiringTime))
  352. {
  353. RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ;
  354. }
  355. }
  356. if (OkayToFreeTheTimer) {
  357. //
  358. // Our caller doesn't want the timer's memory; free it now.
  359. //
  360. RtlpFreeTPHeap(Wait->Timer);
  361. } else {
  362. //
  363. // Our caller wants to keep using the timer's memory, so we
  364. // can't free it; instead, we leave it up to our caller.
  365. //
  366. NOTHING;
  367. }
  368. Wait->Timer = NULL;
  369. } else {
  370. //
  371. // If the wait doesn't have a timer, there's no way our caller
  372. // has the timer, so our caller *should* have set
  373. // OkayToFreeTheTimer. Let's make sure:
  374. //
  375. ASSERT(OkayToFreeTheTimer);
  376. }
  377. // Decrement the (active) wait count
  378. ThreadCB->NumActiveWaits-- ;
  379. InterlockedDecrement( &ThreadCB->NumWaits ) ;
  380. #if DBG
  381. RtlpDumpWaits(ThreadCB);
  382. #endif
  383. RtlInterlockedClearBitsDiscardReturn(&Wait->State,
  384. STATE_ACTIVE);
  385. return STATUS_SUCCESS;
  386. }
  387. NTSTATUS
  388. RtlpDeactivateWait (
  389. PRTLP_WAIT Wait,
  390. BOOLEAN OkayToFreeTheTimer
  391. )
  392. /*++
  393. Routine Description:
  394. This routine is used for deactivating the specified wait. It is executed in a APC.
  395. Arguments:
  396. Wait - The wait to deactivate
  397. OkayToFreeTheTimer - If TRUE, we can delete the wait's timer immediately;
  398. otherwise, we need to keep it around.
  399. Return Value:
  400. --*/
  401. {
  402. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = Wait->ThreadCB ;
  403. ULONG ArrayIndex ; //Index in ActiveWaitArray where the Wait object is placed
  404. ULONG EndIndex = ThreadCB->NumActiveWaits -1;
  405. // get the index in ActiveWaitArray
  406. for (ArrayIndex = 0; ArrayIndex <= EndIndex; ArrayIndex++) {
  407. if (ThreadCB->ActiveWaitPointers[ArrayIndex] == Wait)
  408. break ;
  409. }
  410. if ( ArrayIndex > EndIndex ) {
  411. return STATUS_NOT_FOUND;
  412. }
  413. return RtlpDeactivateWaitWithIndex(Wait, OkayToFreeTheTimer, ArrayIndex);
  414. }
  415. VOID
  416. RtlpProcessWaitCompletion (
  417. PRTLP_WAIT Wait,
  418. ULONG ArrayIndex
  419. )
  420. /*++
  421. Routine Description:
  422. This routine is used for processing a completed wait
  423. Arguments:
  424. Wait - Wait that completed
  425. Return Value:
  426. --*/
  427. {
  428. ULONG TimeRemaining ;
  429. ULONG NewFiringTime ;
  430. LARGE_INTEGER DueTime ;
  431. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB ;
  432. NTSTATUS Status;
  433. ThreadCB = Wait->ThreadCB ;
  434. // deactivate wait if it is meant for single execution
  435. if ( Wait->Flags & WT_EXECUTEONLYONCE ) {
  436. RtlpDeactivateWaitWithIndex (Wait, TRUE, ArrayIndex) ;
  437. }
  438. else {
  439. // if wait being reactivated, then reset the timer now itself as
  440. // it can be deleted in the callback function
  441. if ( Wait->Timer ) {
  442. TimeRemaining = RtlpGetTimeRemaining (ThreadCB->TimerHandle) ;
  443. if (RtlpReOrderDeltaList (
  444. &ThreadCB->TimerQueue.TimerList,
  445. Wait->Timer,
  446. TimeRemaining,
  447. &NewFiringTime,
  448. Wait->Timer->Period)) {
  449. // There is a new element at the head of the queue we need to reset the NT
  450. // timer to fire later
  451. RtlpResetTimer (ThreadCB->TimerHandle, NewFiringTime, ThreadCB) ;
  452. }
  453. }
  454. // move the wait entry to the end, and shift elements to its right one pos towards left
  455. {
  456. HANDLE HandlePtr = ThreadCB->ActiveWaitArray[ArrayIndex];
  457. PRTLP_WAIT WaitPtr = ThreadCB->ActiveWaitPointers[ArrayIndex];
  458. RtlpShiftWaitArray(ThreadCB, ArrayIndex+1, ArrayIndex,
  459. ThreadCB->NumActiveWaits -1 - ArrayIndex)
  460. ThreadCB->ActiveWaitArray[ThreadCB->NumActiveWaits-1] = HandlePtr ;
  461. ThreadCB->ActiveWaitPointers[ThreadCB->NumActiveWaits-1] = WaitPtr ;
  462. }
  463. }
  464. // call callback function (FALSE since this isnt a timeout related callback)
  465. if ( Wait->Flags & WT_EXECUTEINWAITTHREAD ) {
  466. // executing callback after RtlpDeactivateWait allows the Callback to call
  467. // RtlDeregisterWait Wait->RefCount is not incremented so that RtlDeregisterWait
  468. // will work on this Wait. Though Wait->RefCount is not incremented, others cannot
  469. // deregister this Wait as it has to be queued as an APC.
  470. #if DBG
  471. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  472. RTLP_THREADPOOL_TRACE_MASK,
  473. "<%d> Calling WaitOrTimer(wait) %p: Handle %p fn:%p context:%p bool:%d Thread<%x:%x>\n",
  474. Wait->DbgId,
  475. Wait,
  476. Wait->WaitHandle,
  477. Wait->Function, Wait->Context,
  478. FALSE,
  479. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  480. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
  481. #endif
  482. RtlpWaitOrTimerCallout(Wait->Function,
  483. Wait->Context,
  484. FALSE,
  485. Wait->ActivationContext,
  486. Wait->ImpersonationToken);
  487. // Wait object could have been deleted in the above callback
  488. return ;
  489. } else {
  490. InterlockedIncrement( &Wait->RefCount ) ;
  491. Status = RtlQueueWorkItem(
  492. RtlpAsyncWaitCallbackCompletion,
  493. Wait,
  494. Wait->Flags );
  495. if (!NT_SUCCESS(Status)) {
  496. // NTRAID#202802-2000/10/12-earhart: we really ought
  497. // to deal with this case in a better way, since we
  498. // can't guarantee (with our current architecture)
  499. // that the enqueue will work.
  500. if ( InterlockedDecrement( &Wait->RefCount ) == 0 ) {
  501. RtlpDeleteWait( Wait ) ;
  502. }
  503. }
  504. }
  505. }
  506. LONG
  507. RtlpWaitThread (
  508. PVOID Parameter
  509. )
  510. /*++
  511. Routine Description:
  512. This routine is used for all waits in the wait thread pool
  513. Arguments:
  514. HandlePtr - Pointer to our handle
  515. Return Value:
  516. Nothing. The thread never terminates.
  517. N.B. This thread is started while another thread (calling
  518. RtlpStartWaitThread) holds the WaitCriticalSection;
  519. WaitCriticalSection will be held until WaitThreadStartedEvent
  520. is set.
  521. --*/
  522. {
  523. ULONG i ; // Used as an index
  524. NTSTATUS Status ;
  525. LARGE_INTEGER TimeOut; // Timeout used for waits
  526. PLARGE_INTEGER TimeOutPtr; // Pointer to timeout
  527. RTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCBBuf; // Control Block
  528. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = &ThreadCBBuf;
  529. #define WAIT_IDLE_TIMEOUT 400000
  530. UNREFERENCED_PARAMETER(Parameter);
  531. #if DBG
  532. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  533. RTLP_THREADPOOL_TRACE_MASK,
  534. "Starting wait thread\n");
  535. #endif
  536. // Initialize thread control block
  537. RtlZeroMemory(&ThreadCBBuf, sizeof(ThreadCBBuf));
  538. InitializeListHead (&ThreadCB->WaitThreadsList) ;
  539. ThreadCB->ThreadHandle = WaitThreadHandle;
  540. ThreadCB->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  541. RtlZeroMemory (&ThreadCB->ActiveWaitArray[0], sizeof (HANDLE) * MAXIMUM_WAIT_OBJECTS) ;
  542. RtlZeroMemory (&ThreadCB->ActiveWaitPointers[0], sizeof (HANDLE) * MAXIMUM_WAIT_OBJECTS) ;
  543. ThreadCB->TimerHandle = WaitThreadTimer;
  544. ThreadCB->Firing64BitTickCount = 0 ;
  545. ThreadCB->Current64BitTickCount.QuadPart = NtGetTickCount() ;
  546. // Reset the NT Timer to never fire initially
  547. RtlpResetTimer (ThreadCB->TimerHandle, -1, ThreadCB) ;
  548. InitializeListHead (&ThreadCB->TimerQueue.TimerList) ;
  549. InitializeListHead (&ThreadCB->TimerQueue.UncancelledTimerList) ;
  550. // Insert this new wait thread in the WaitThreads list. Insert at the head so that
  551. // the request that caused this thread to be created can find it right away.
  552. InsertHeadList (&WaitThreads, &ThreadCB->WaitThreadsList) ;
  553. // The first wait element is the timer object
  554. ThreadCB->ActiveWaitArray[0] = ThreadCB->TimerHandle ;
  555. ThreadCB->NumActiveWaits = ThreadCB->NumWaits = 1 ;
  556. ThreadCB->NumRegisteredWaits = 0;
  557. // till here, the function is running under the global wait lock
  558. // We are all initialized now. Notify the starter to queue the task.
  559. NtSetEvent(WaitThreadStartedEvent, NULL);
  560. // Loop forever - wait threads never, never die.
  561. for ( ; ; ) {
  562. if (ThreadCB->NumActiveWaits == 1
  563. && ThreadCB->NumRegisteredWaits == 0) {
  564. // It we don't have anything depending on us, and we're
  565. // idle for a while, it might be good for us to go away.
  566. TimeOut.QuadPart = Int32x32To64( WAIT_IDLE_TIMEOUT, -10000 ) ;
  567. TimeOutPtr = &TimeOut;
  568. } else {
  569. // We need to stick around.
  570. TimeOutPtr = NULL;
  571. }
  572. Status = NtWaitForMultipleObjects (
  573. (CHAR) ThreadCB->NumActiveWaits,
  574. ThreadCB->ActiveWaitArray,
  575. WaitAny,
  576. TRUE, // Wait Alertably
  577. TimeOutPtr
  578. ) ;
  579. if (Status == STATUS_ALERTED || Status == STATUS_USER_APC) {
  580. continue ;
  581. } else if (Status >= STATUS_WAIT_0 && Status <= STATUS_WAIT_63) {
  582. if (Status == STATUS_WAIT_0) {
  583. RtlpProcessTimeouts (ThreadCB) ;
  584. } else {
  585. // Wait completed call Callback function
  586. RtlpProcessWaitCompletion (
  587. ThreadCB->ActiveWaitPointers[Status], Status) ;
  588. }
  589. } else if (Status >= STATUS_ABANDONED_WAIT_0
  590. && Status <= STATUS_ABANDONED_WAIT_63) {
  591. PRTLP_WAIT Wait = ThreadCB->ActiveWaitPointers[Status - STATUS_ABANDONED_WAIT_0];
  592. #if DBG
  593. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  594. RTLP_THREADPOOL_ERROR_MASK,
  595. "<%d> Abandoned wait %p: index:%d Handle:%p\n",
  596. Wait->DbgId,
  597. Wait,
  598. Status - STATUS_ABANDONED_WAIT_0,
  599. ThreadCB->ActiveWaitArray[Status - STATUS_ABANDONED_WAIT_0]);
  600. #endif
  601. // Abandoned wait -- nuke it.
  602. // ISSUE-2000/10/13-earhart: It would be ideal to do
  603. // something with the mutex -- maybe release it? If we
  604. // could abandon it as we release it, that would be ideal.
  605. // This is not a good situation. Maybe we could call
  606. // NtReleaseMutant when we register the object, failing
  607. // the registration unless we get back
  608. // STATUS_OBJECT_TYPE_MISMATCH. Although who knows
  609. // whether someone's having wait threads wait for mutexes,
  610. // doing their lock processing inside the callback?
  611. RtlpDeactivateWaitWithIndex(Wait,
  612. TRUE,
  613. Status - STATUS_ABANDONED_WAIT_0);
  614. } else if (Status == STATUS_TIMEOUT) {
  615. //
  616. // remove this thread from the wait list and terminate
  617. //
  618. {
  619. ULONG NumWaits;
  620. ACQUIRE_GLOBAL_WAIT_LOCK() ;
  621. NumWaits = ThreadCB->NumWaits;
  622. if (ThreadCB->NumWaits <= 1) {
  623. RemoveEntryList(&ThreadCB->WaitThreadsList) ;
  624. NtClose(ThreadCB->ThreadHandle) ;
  625. NtClose(ThreadCB->TimerHandle) ;
  626. }
  627. RELEASE_GLOBAL_WAIT_LOCK() ;
  628. if (NumWaits <= 1) {
  629. RtlpExitThreadFunc( 0 );
  630. }
  631. }
  632. } else if (Status == STATUS_INSUFFICIENT_RESOURCES) {
  633. //
  634. // Delay for a short period of time, and retry the wait.
  635. //
  636. TimeOut.QuadPart = UInt32x32To64( 10 /* Milliseconds to sleep */,
  637. 10000 /* Milliseconds to 100 Nanoseconds) */);
  638. TimeOut.QuadPart *= -1; /* Make it a relative time */
  639. NtDelayExecution(TRUE, &TimeOut);
  640. } else {
  641. // Some other error: scan for bad object handles and keep going.
  642. ULONG i ;
  643. #if DBG
  644. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  645. RTLP_THREADPOOL_WARNING_MASK,
  646. "Application broke an object handle "
  647. "that the wait thread was waiting on: Code:%x ThreadId:<%x:%x>\n",
  648. Status, HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  649. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  650. #endif
  651. TimeOut.QuadPart = 0 ;
  652. for (i = 0;
  653. i < ThreadCB->NumActiveWaits;
  654. i++) {
  655. Status = NtWaitForSingleObject(
  656. ThreadCB->ActiveWaitArray[i],
  657. FALSE, // Don't bother being alertable
  658. &TimeOut // Just poll
  659. ) ;
  660. if (Status == STATUS_SUCCESS) {
  661. // We succeded our wait here; we need to issue its
  662. // callout now (in case waiting on it changes its
  663. // status -- auto-reset events, mutexes, &c...)
  664. if (i == 0) {
  665. RtlpProcessTimeouts(ThreadCB);
  666. } else {
  667. RtlpProcessWaitCompletion(ThreadCB->ActiveWaitPointers[i], i);
  668. }
  669. // In case that causes us to take any APCs, let's
  670. // drop out and go back to waiting.
  671. break;
  672. } else if (Status == STATUS_USER_APC) {
  673. // It's not worth going on after this -- bail.
  674. break;
  675. } else if (Status != STATUS_TIMEOUT) {
  676. #if DBG
  677. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  678. Status == STATUS_ABANDONED_WAIT_0
  679. ? RTLP_THREADPOOL_ERROR_MASK
  680. : RTLP_THREADPOOL_WARNING_MASK,
  681. "<%d> %s: index:%d Handle:%p WaitEntry Ptr:%p\n",
  682. ThreadCB->ActiveWaitPointers[i]->DbgId,
  683. Status == STATUS_ABANDONED_WAIT_0
  684. ? "Abandoned wait"
  685. : "Deactivating invalid handle",
  686. i,
  687. ThreadCB->ActiveWaitArray[i],
  688. ThreadCB->ActiveWaitPointers[i] ) ;
  689. #endif
  690. RtlpDeactivateWaitWithIndex(ThreadCB->ActiveWaitPointers[i],
  691. TRUE,
  692. i);
  693. break;
  694. }
  695. } // loop over active waits
  696. }
  697. } // forever
  698. return 0 ; // Keep compiler happy
  699. }
  700. NTSTATUS
  701. RtlpStartWaitThread(
  702. VOID
  703. )
  704. // N.B. The WaitCriticalSection MUST be held when calling this function
  705. {
  706. NTSTATUS Status;
  707. PRTLP_EVENT Event = NULL;
  708. WaitThreadTimer = NULL;
  709. WaitThreadStartedEvent = NULL;
  710. Event = RtlpGetWaitEvent();
  711. if (! Event) {
  712. Status = STATUS_NO_MEMORY;
  713. goto fail;
  714. }
  715. WaitThreadStartedEvent = Event->Handle;
  716. Status = NtCreateTimer(&WaitThreadTimer,
  717. TIMER_ALL_ACCESS,
  718. NULL,
  719. NotificationTimer);
  720. if (! NT_SUCCESS(Status)) {
  721. goto fail;
  722. }
  723. Status = RtlpStartThreadpoolThread (RtlpWaitThread, NULL, &WaitThreadHandle);
  724. if (! NT_SUCCESS(Status)) {
  725. goto fail;
  726. }
  727. Status = NtWaitForSingleObject(WaitThreadStartedEvent, FALSE, NULL);
  728. if (! NT_SUCCESS(Status)) {
  729. goto fail;
  730. }
  731. RtlpFreeWaitEvent(Event);
  732. WaitThreadTimer = NULL;
  733. WaitThreadStartedEvent = NULL;
  734. return STATUS_SUCCESS;
  735. fail:
  736. if (WaitThreadTimer) {
  737. NtClose(WaitThreadTimer);
  738. }
  739. if (Event) {
  740. RtlpFreeWaitEvent(Event);
  741. }
  742. WaitThreadTimer = NULL;
  743. WaitThreadStartedEvent = NULL;
  744. return Status;
  745. }
  746. NTSTATUS
  747. RtlpFindWaitThread (
  748. PRTLP_WAIT_THREAD_CONTROL_BLOCK *ThreadCB
  749. )
  750. /*++
  751. Routine Description:
  752. Walks thru the list of wait threads and finds one which can accomodate another wait.
  753. If one is not found then a new thread is created.
  754. This routine assumes that the caller has the GlobalWaitLock.
  755. Arguments:
  756. ThreadCB: returns the ThreadCB of the wait thread that will service the wait request.
  757. Return Value:
  758. STATUS_SUCCESS if a wait thread was allocated,
  759. --*/
  760. {
  761. NTSTATUS Status = STATUS_SUCCESS;
  762. PLIST_ENTRY Node ;
  763. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCBTmp;
  764. ACQUIRE_GLOBAL_WAIT_LOCK() ;
  765. do {
  766. // Walk thru the list of Wait Threads and find a Wait thread that can accomodate a
  767. // new wait request.
  768. // *Consider* finding a wait thread with least # of waits to facilitate better
  769. // load balancing of waits.
  770. for (Node = WaitThreads.Flink ; Node != &WaitThreads ; Node = Node->Flink) {
  771. ThreadCBTmp = CONTAINING_RECORD (Node, RTLP_WAIT_THREAD_CONTROL_BLOCK,
  772. WaitThreadsList) ;
  773. // Wait Threads can accomodate up to MAXIMUM_WAIT_OBJECTS
  774. // waits (NtWaitForMultipleObjects limit)
  775. if ((ThreadCBTmp)->NumWaits < MAXIMUM_WAIT_OBJECTS) {
  776. // Found a thread with some wait slots available.
  777. InterlockedIncrement ( &(ThreadCBTmp)->NumWaits) ;
  778. *ThreadCB = ThreadCBTmp;
  779. RELEASE_GLOBAL_WAIT_LOCK() ;
  780. return STATUS_SUCCESS ;
  781. }
  782. }
  783. // If we reach here, we dont have any more wait threads. so create a new wait thread.
  784. Status = RtlpStartWaitThread();
  785. // If thread creation fails then return the failure to caller
  786. if (! NT_SUCCESS(Status) ) {
  787. #if DBG
  788. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  789. RTLP_THREADPOOL_WARNING_MASK,
  790. "ThreadPool could not create wait thread\n");
  791. #endif
  792. RELEASE_GLOBAL_WAIT_LOCK() ;
  793. return Status ;
  794. }
  795. // Loop back now that we have created another thread
  796. } while (TRUE) ; // Loop back to top and put new wait request in the newly created thread
  797. RELEASE_GLOBAL_WAIT_LOCK() ;
  798. return Status ;
  799. }
  800. VOID
  801. RtlpWaitReleaseWorker(ULONG Flags)
  802. {
  803. if (! (Flags & WT_EXECUTEINWAITTHREAD)) {
  804. RtlpReleaseWorker(Flags);
  805. }
  806. }
  807. NTSTATUS
  808. RtlRegisterWait (
  809. OUT PHANDLE WaitHandle,
  810. IN HANDLE Handle,
  811. IN WAITORTIMERCALLBACKFUNC Function,
  812. IN PVOID Context,
  813. IN ULONG Milliseconds,
  814. IN ULONG Flags
  815. )
  816. /*++
  817. Routine Description:
  818. This routine adds a new wait request to the pool of objects being waited on.
  819. Arguments:
  820. WaitHandle - Handle returned on successful completion of this routine.
  821. Handle - Handle to the object to be waited on
  822. Function - Routine that is called when the wait completes or a timeout occurs
  823. Context - Opaque pointer passed in as an argument to Function
  824. Milliseconds - Timeout for the wait in milliseconds. 0xffffffff means dont
  825. timeout.
  826. Flags - Can be one of:
  827. WT_EXECUTEINWAITTHREAD - if WorkerProc should be invoked in the wait
  828. thread itself. This should only be used for small routines.
  829. WT_EXECUTEINIOTHREAD - use only if the WorkerProc should be invoked in
  830. an IO Worker thread. Avoid using it.
  831. If Flags is not WT_EXECUTEINWAITTHREAD, the following flag can also be set:
  832. WT_EXECUTELONGFUNCTION - indicates that the callback might be blocked
  833. for a long duration. Use only if the callback is being queued to a
  834. worker thread.
  835. Return Value:
  836. NTSTATUS - Result code from call. The following are returned
  837. STATUS_SUCCESS - The registration was successful.
  838. STATUS_NO_MEMORY - There was not sufficient heap to perform the requested
  839. operation.
  840. or other NTSTATUS error code
  841. --*/
  842. {
  843. PRTLP_WAIT Wait ;
  844. NTSTATUS Status ;
  845. PRTLP_EVENT Event ;
  846. LARGE_INTEGER TimeOut ;
  847. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB = NULL;
  848. PRTLP_GENERIC_TIMER Timer;
  849. *WaitHandle = NULL ;
  850. if (LdrpShutdownInProgress) {
  851. return STATUS_UNSUCCESSFUL;
  852. }
  853. // Initialize thread pool if it isnt already done
  854. if ( CompletedWaitInitialization != 1) {
  855. Status = RtlpInitializeWaitThreadPool () ;
  856. if (! NT_SUCCESS( Status ) )
  857. return Status ;
  858. }
  859. if (Handle == NtCurrentThread()
  860. || Handle == NtCurrentProcess()) {
  861. return STATUS_INVALID_PARAMETER_2;
  862. }
  863. if (Flags&0xffff0000) {
  864. MaxThreads = (Flags & 0xffff0000)>>16;
  865. }
  866. // Initialize Wait request
  867. if (! (Flags & WT_EXECUTEINWAITTHREAD)) {
  868. Status = RtlpAcquireWorker(Flags);
  869. if (! NT_SUCCESS(Status)) {
  870. return Status;
  871. }
  872. }
  873. Wait = (PRTLP_WAIT) RtlpAllocateTPHeap ( sizeof (RTLP_WAIT),
  874. HEAP_ZERO_MEMORY) ;
  875. if (!Wait) {
  876. RtlpWaitReleaseWorker(Flags);
  877. return STATUS_NO_MEMORY ;
  878. }
  879. Wait->Timer = NULL;
  880. if (Milliseconds != INFINITE_TIME) {
  881. Timer = RtlpAllocateTPHeap(sizeof(RTLP_GENERIC_TIMER),
  882. HEAP_ZERO_MEMORY);
  883. if (! Timer) {
  884. RtlpFreeTPHeap(Wait);
  885. RtlpWaitReleaseWorker(Flags);
  886. return STATUS_NO_MEMORY;
  887. }
  888. } else {
  889. Timer = NULL;
  890. }
  891. if (NtCurrentTeb()->IsImpersonating) {
  892. Status = NtOpenThreadToken(NtCurrentThread(),
  893. MAXIMUM_ALLOWED,
  894. TRUE,
  895. &Wait->ImpersonationToken);
  896. if (! NT_SUCCESS(Status)) {
  897. if (Timer) {
  898. RtlpFreeTPHeap(Timer);
  899. }
  900. RtlpFreeTPHeap(Wait);
  901. RtlpWaitReleaseWorker(Flags);
  902. return Status;
  903. }
  904. } else {
  905. Wait->ImpersonationToken = NULL;
  906. }
  907. Status = RtlGetActiveActivationContext(&Wait->ActivationContext);
  908. if (!NT_SUCCESS(Status)) {
  909. if (Status == STATUS_SXS_THREAD_QUERIES_DISABLED) {
  910. Wait->ActivationContext = INVALID_ACTIVATION_CONTEXT;
  911. Status = STATUS_SUCCESS;
  912. } else {
  913. if (Wait->ImpersonationToken) {
  914. NtClose(Wait->ImpersonationToken);
  915. }
  916. if (Timer) {
  917. RtlpFreeTPHeap(Timer);
  918. }
  919. RtlpFreeTPHeap(Wait);
  920. RtlpWaitReleaseWorker(Flags);
  921. return Status;
  922. }
  923. }
  924. Wait->WaitHandle = Handle ;
  925. Wait->Flags = Flags ;
  926. Wait->Function = Function ;
  927. Wait->Context = Context ;
  928. Wait->Timeout = Milliseconds ;
  929. SET_WAIT_SIGNATURE(Wait) ;
  930. // timer part of wait is initialized by wait thread in RtlpAddWait
  931. // Get a wait thread that can accomodate another wait request.
  932. Status = RtlpFindWaitThread (&ThreadCB) ;
  933. if (! NT_SUCCESS(Status)) {
  934. if (Wait->ActivationContext != INVALID_ACTIVATION_CONTEXT) {
  935. RtlReleaseActivationContext(Wait->ActivationContext);
  936. }
  937. if (Wait->ImpersonationToken) {
  938. NtClose(Wait->ImpersonationToken);
  939. }
  940. CLEAR_SIGNATURE(Wait);
  941. if (Timer) {
  942. RtlpFreeTPHeap(Timer);
  943. }
  944. RtlpFreeTPHeap(Wait) ;
  945. RtlpWaitReleaseWorker(Flags);
  946. return Status ;
  947. }
  948. Wait->ThreadCB = ThreadCB ;
  949. #if DBG1
  950. Wait->DbgId = ++NextWaitDbgId ;
  951. Wait->ThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  952. #endif
  953. #if DBG
  954. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  955. RTLP_THREADPOOL_TRACE_MASK,
  956. "<%d:%d> Wait %p (Handle %p) created by thread:<%x:%x>; queueing APC\n",
  957. Wait->DbgId, 1, Wait, Wait->WaitHandle,
  958. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  959. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  960. #endif
  961. // Queue an APC to the Wait Thread
  962. Status = NtQueueApcThread(
  963. ThreadCB->ThreadHandle,
  964. (PPS_APC_ROUTINE)RtlpAddWait,
  965. (PVOID)Wait,
  966. (PVOID)Timer,
  967. NULL
  968. );
  969. if ( NT_SUCCESS(Status) ) {
  970. Status = STATUS_SUCCESS ;
  971. *WaitHandle = Wait ;
  972. } else {
  973. CLEAR_SIGNATURE(Wait);
  974. InterlockedDecrement(&ThreadCB->NumWaits);
  975. if (Wait->ActivationContext != INVALID_ACTIVATION_CONTEXT) {
  976. RtlReleaseActivationContext(Wait->ActivationContext);
  977. }
  978. if (Wait->ImpersonationToken) {
  979. NtClose(Wait->ImpersonationToken);
  980. }
  981. if (Timer) {
  982. RtlpFreeTPHeap(Timer);
  983. }
  984. RtlpFreeTPHeap( Wait ) ;
  985. RtlpWaitReleaseWorker(Flags);
  986. }
  987. return Status ;
  988. }
  989. NTSTATUS
  990. RtlpDeregisterWait (
  991. PRTLP_WAIT Wait,
  992. HANDLE PartialCompletionEvent,
  993. PULONG RetStatusPtr
  994. )
  995. /*++
  996. Routine Description:
  997. This routine is used for deregistering the specified wait.
  998. Arguments:
  999. Wait - The wait to deregister
  1000. Return Value:
  1001. --*/
  1002. {
  1003. ULONG Status = STATUS_SUCCESS ;
  1004. ULONG DontUse ;
  1005. PULONG RetStatus = RetStatusPtr ? RetStatusPtr : &DontUse;
  1006. CHECK_SIGNATURE(Wait) ;
  1007. #if DBG
  1008. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1009. RTLP_THREADPOOL_TRACE_MASK,
  1010. "<%d> Deregistering Wait %p (Handle %p) in thread %x\n",
  1011. Wait->DbgId,
  1012. Wait,
  1013. Wait->WaitHandle,
  1014. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread)) ;
  1015. #endif
  1016. // RtlpDeregisterWait can be called on a wait that has not yet been
  1017. // registered. This indicates that someone calls a RtlDeregisterWait
  1018. // inside a WaitThreadCallback for a Wait other than that was fired.
  1019. // Application bug!! We handle it
  1020. if ( ! (Wait->State & STATE_REGISTERED) ) {
  1021. // set state to deleted, so that it does not get registered
  1022. RtlInterlockedSetBitsDiscardReturn(&Wait->State,
  1023. STATE_DELETE);
  1024. InterlockedDecrement( &Wait->RefCount );
  1025. if ( PartialCompletionEvent ) {
  1026. NtSetEvent( PartialCompletionEvent, NULL ) ;
  1027. }
  1028. *RetStatus = STATUS_SUCCESS ;
  1029. return STATUS_SUCCESS ;
  1030. }
  1031. // deactivate wait.
  1032. if ( Wait->State & STATE_ACTIVE ) {
  1033. if ( ! NT_SUCCESS( RtlpDeactivateWait ( Wait, TRUE ) ) ) {
  1034. *RetStatus = STATUS_NOT_FOUND ;
  1035. return STATUS_NOT_FOUND ;
  1036. }
  1037. }
  1038. // Deregister wait and set delete bit
  1039. RtlInterlockedClearBitsDiscardReturn(&Wait->State,
  1040. STATE_REGISTERED);
  1041. RtlInterlockedSetBitsDiscardReturn(&Wait->State,
  1042. STATE_DELETE);
  1043. ASSERT(Wait->ThreadCB->NumRegisteredWaits > 0);
  1044. Wait->ThreadCB->NumRegisteredWaits--;
  1045. // We can no longer guarantee that the wait thread will be around;
  1046. // clear the wait's ThreadCB to make it obvious if we attempt to
  1047. // make use of it.
  1048. Wait->ThreadCB = NULL;
  1049. // delete wait if RefCount == 0
  1050. if ( InterlockedDecrement (&Wait->RefCount) == 0 ) {
  1051. RtlpDeleteWait ( Wait ) ;
  1052. Status = *RetStatus = STATUS_SUCCESS ;
  1053. } else {
  1054. Status = *RetStatus = STATUS_PENDING ;
  1055. }
  1056. if ( PartialCompletionEvent ) {
  1057. NtSetEvent( PartialCompletionEvent, NULL ) ;
  1058. }
  1059. return Status ;
  1060. }
  1061. NTSTATUS
  1062. RtlDeregisterWaitEx(
  1063. IN HANDLE WaitHandle,
  1064. IN HANDLE Event
  1065. )
  1066. /*++
  1067. Routine Description:
  1068. This routine removes the specified wait from the pool of objects being
  1069. waited on. Once this call returns, no new Callbacks will be invoked.
  1070. Depending on the value of Event, the call can be blocking or non-blocking.
  1071. Blocking calls MUST NOT be invoked inside the callback routines, except
  1072. when a callback being executed in the Wait thread context deregisters
  1073. its associated Wait (in this case there is no reason for making blocking calls),
  1074. or when a callback queued to a worker thread is deregistering some other wait item
  1075. (be careful of deadlocks here).
  1076. Arguments:
  1077. WaitHandle - Handle indentifying the wait.
  1078. Event - Event to wait upon.
  1079. (HANDLE)-1: The function creates an event and waits on it.
  1080. Event : The caller passes an Event. The function removes the wait handle,
  1081. but does not wait for all callbacks to complete. The Event is
  1082. released after all callbacks have completed.
  1083. NULL : The function is non-blocking. The function removes the wait handle,
  1084. but does not wait for all callbacks to complete.
  1085. Return Value:
  1086. STATUS_SUCCESS - The deregistration was successful.
  1087. STATUS_PENDING - Some callback is still pending.
  1088. --*/
  1089. {
  1090. NTSTATUS Status, StatusAsync = STATUS_SUCCESS ;
  1091. PRTLP_WAIT Wait = (PRTLP_WAIT) WaitHandle ;
  1092. ULONG CurrentThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) ;
  1093. PRTLP_EVENT CompletionEvent = NULL ;
  1094. HANDLE ThreadHandle ;
  1095. ULONG NonBlocking = ( Event != (HANDLE) -1 ) ; //The call returns non-blocking
  1096. #if DBG
  1097. ULONG WaitDbgId;
  1098. HANDLE Handle;
  1099. #endif
  1100. if (LdrpShutdownInProgress) {
  1101. return STATUS_SUCCESS;
  1102. }
  1103. if (!Wait) {
  1104. return STATUS_INVALID_PARAMETER_1 ;
  1105. }
  1106. ThreadHandle = Wait->ThreadCB->ThreadHandle ;
  1107. CHECK_DEL_SIGNATURE( Wait ) ;
  1108. SET_DEL_SIGNATURE( Wait ) ;
  1109. #if DBG1
  1110. Wait->ThreadId2 = CurrentThreadId ;
  1111. #endif
  1112. if (Event == (HANDLE)-1) {
  1113. // Get an event from the event cache
  1114. CompletionEvent = RtlpGetWaitEvent () ;
  1115. if (!CompletionEvent) {
  1116. return STATUS_NO_MEMORY ;
  1117. }
  1118. }
  1119. Wait = (PRTLP_WAIT) WaitHandle ;
  1120. #if DBG
  1121. WaitDbgId = Wait->DbgId;
  1122. Handle = Wait->WaitHandle;
  1123. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1124. RTLP_THREADPOOL_TRACE_MASK,
  1125. "<%d:%d> Wait %p (Handle %p) deregistering by thread:<%x:%x>\n",
  1126. WaitDbgId,
  1127. Wait->RefCount,
  1128. Wait,
  1129. Handle,
  1130. CurrentThreadId,
  1131. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  1132. #endif
  1133. Wait->CompletionEvent = CompletionEvent
  1134. ? CompletionEvent->Handle
  1135. : Event ;
  1136. //
  1137. // RtlDeregisterWaitEx is being called from within the Wait thread callback
  1138. //
  1139. if ( CurrentThreadId == Wait->ThreadCB->ThreadId ) {
  1140. Status = RtlpDeregisterWait ( Wait, NULL, NULL ) ;
  1141. // all callback functions run in the wait thread. So cannot return PENDING
  1142. ASSERT ( Status != STATUS_PENDING ) ;
  1143. } else {
  1144. PRTLP_EVENT PartialCompletionEvent = NULL ;
  1145. if (NonBlocking) {
  1146. PartialCompletionEvent = RtlpGetWaitEvent () ;
  1147. if (!PartialCompletionEvent) {
  1148. return STATUS_NO_MEMORY ;
  1149. }
  1150. }
  1151. // Queue an APC to the Wait Thread
  1152. Status = NtQueueApcThread(
  1153. Wait->ThreadCB->ThreadHandle,
  1154. (PPS_APC_ROUTINE)RtlpDeregisterWait,
  1155. (PVOID) Wait,
  1156. NonBlocking ? PartialCompletionEvent->Handle : NULL ,
  1157. NonBlocking ? (PVOID)&StatusAsync : NULL
  1158. );
  1159. if (! NT_SUCCESS(Status)) {
  1160. if (CompletionEvent) RtlpFreeWaitEvent( CompletionEvent ) ;
  1161. if (PartialCompletionEvent) RtlpFreeWaitEvent( PartialCompletionEvent ) ;
  1162. return Status ;
  1163. }
  1164. // Block till the wait entry has been deactivated
  1165. if (NonBlocking) {
  1166. Status = RtlpWaitForEvent( PartialCompletionEvent->Handle, ThreadHandle ) ;
  1167. }
  1168. if (PartialCompletionEvent) RtlpFreeWaitEvent( PartialCompletionEvent ) ;
  1169. }
  1170. if ( CompletionEvent ) {
  1171. // wait for Event to be fired. Return if the thread has been killed.
  1172. #if DBG
  1173. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1174. RTLP_THREADPOOL_TRACE_MASK,
  1175. "<%d> Wait %p (Handle %p) deregister waiting ThreadId<%x:%x>\n",
  1176. WaitDbgId,
  1177. Wait,
  1178. Handle,
  1179. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  1180. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) ;
  1181. #endif
  1182. Status = RtlpWaitForEvent( CompletionEvent->Handle, ThreadHandle ) ;
  1183. #if DBG
  1184. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1185. RTLP_THREADPOOL_TRACE_MASK,
  1186. "<%d> Wait %p (Handle %p) deregister completed\n",
  1187. WaitDbgId,
  1188. Wait,
  1189. Handle) ;
  1190. #endif
  1191. RtlpFreeWaitEvent( CompletionEvent ) ;
  1192. return NT_SUCCESS( Status ) ? STATUS_SUCCESS : Status ;
  1193. } else {
  1194. return StatusAsync ;
  1195. }
  1196. }
  1197. NTSTATUS
  1198. RtlDeregisterWait(
  1199. IN HANDLE WaitHandle
  1200. )
  1201. /*++
  1202. Routine Description:
  1203. This routine removes the specified wait from the pool of objects being
  1204. waited on. This routine is non-blocking. Once this call returns, no new
  1205. Callbacks are invoked. However, Callbacks that might already have been queued
  1206. to worker threads are not cancelled.
  1207. Arguments:
  1208. WaitHandle - Handle indentifying the wait.
  1209. Return Value:
  1210. STATUS_SUCCESS - The deregistration was successful.
  1211. STATUS_PENDING - Some callbacks associated with this Wait, are still executing.
  1212. --*/
  1213. {
  1214. return RtlDeregisterWaitEx( WaitHandle, NULL ) ;
  1215. }
  1216. VOID
  1217. RtlpDeleteWait (
  1218. PRTLP_WAIT Wait
  1219. )
  1220. /*++
  1221. Routine Description:
  1222. This routine is used for deleting the specified wait. It can be executed
  1223. outside the context of the wait thread. So structure except the WaitEntry
  1224. can be changed. It also sets the event.
  1225. Arguments:
  1226. Wait - The wait to delete
  1227. Return Value:
  1228. --*/
  1229. {
  1230. CHECK_SIGNATURE( Wait ) ;
  1231. CLEAR_SIGNATURE( Wait ) ;
  1232. #if DBG
  1233. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  1234. RTLP_THREADPOOL_TRACE_MASK,
  1235. "<%d> Wait %p (Handle %p) deleted in thread:%x\n", Wait->DbgId,
  1236. Wait,
  1237. Wait->WaitHandle,
  1238. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread)) ;
  1239. #endif
  1240. if ( Wait->CompletionEvent ) {
  1241. NtSetEvent( Wait->CompletionEvent, NULL ) ;
  1242. }
  1243. RtlpWaitReleaseWorker(Wait->Flags);
  1244. if (Wait->ActivationContext != INVALID_ACTIVATION_CONTEXT)
  1245. RtlReleaseActivationContext(Wait->ActivationContext);
  1246. if (Wait->ImpersonationToken) {
  1247. NtClose(Wait->ImpersonationToken);
  1248. }
  1249. RtlpFreeTPHeap( Wait) ;
  1250. return ;
  1251. }
  1252. NTSTATUS
  1253. RtlpWaitCleanup(
  1254. VOID
  1255. )
  1256. {
  1257. PLIST_ENTRY Node;
  1258. HANDLE TmpHandle;
  1259. BOOLEAN Cleanup;
  1260. IS_COMPONENT_INITIALIZED(StartedWaitInitialization,
  1261. CompletedWaitInitialization,
  1262. Cleanup ) ;
  1263. if ( Cleanup ) {
  1264. PRTLP_WAIT_THREAD_CONTROL_BLOCK ThreadCB ;
  1265. ACQUIRE_GLOBAL_WAIT_LOCK() ;
  1266. // Queue an APC to all Wait Threads
  1267. for (Node = WaitThreads.Flink ; Node != &WaitThreads ;
  1268. Node = Node->Flink)
  1269. {
  1270. ThreadCB = CONTAINING_RECORD(Node,
  1271. RTLP_WAIT_THREAD_CONTROL_BLOCK,
  1272. WaitThreadsList) ;
  1273. if ( ThreadCB->NumWaits != 0 ) {
  1274. RELEASE_GLOBAL_WAIT_LOCK( ) ;
  1275. return STATUS_UNSUCCESSFUL ;
  1276. }
  1277. RemoveEntryList( &ThreadCB->WaitThreadsList ) ;
  1278. TmpHandle = ThreadCB->ThreadHandle ;
  1279. NtQueueApcThread(
  1280. ThreadCB->ThreadHandle,
  1281. (PPS_APC_ROUTINE)RtlpThreadCleanup,
  1282. NULL,
  1283. NULL,
  1284. NULL
  1285. );
  1286. NtClose( TmpHandle ) ;
  1287. }
  1288. RELEASE_GLOBAL_WAIT_LOCK( ) ;
  1289. }
  1290. return STATUS_SUCCESS;
  1291. }