[NTOS] Limit the spam from NtNotifyChangeMultipleKeys.
[reactos.git] / ntoskrnl / ke / wait.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/wait.c
5 * PURPOSE: Manages waiting for Dispatcher Objects
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Gunnar Dalsnes
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PRIVATE FUNCTIONS *********************************************************/
17
18 VOID
19 FASTCALL
20 KiWaitTest(IN PVOID ObjectPointer,
21 IN KPRIORITY Increment)
22 {
23 PLIST_ENTRY WaitEntry, WaitList;
24 PKWAIT_BLOCK WaitBlock;
25 PKTHREAD WaitThread;
26 PKMUTANT FirstObject = ObjectPointer;
27 NTSTATUS WaitStatus;
28
29 /* Loop the Wait Entries */
30 WaitList = &FirstObject->Header.WaitListHead;
31 WaitEntry = WaitList->Flink;
32 while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList))
33 {
34 /* Get the current wait block */
35 WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
36 WaitThread = WaitBlock->Thread;
37 WaitStatus = STATUS_KERNEL_APC;
38
39 /* Check the current Wait Mode */
40 if (WaitBlock->WaitType == WaitAny)
41 {
42 /* Easy case, satisfy only this wait */
43 WaitStatus = (NTSTATUS)WaitBlock->WaitKey;
44 KiSatisfyObjectWait(FirstObject, WaitThread);
45 }
46
47 /* Now do the rest of the unwait */
48 KiUnwaitThread(WaitThread, WaitStatus, Increment);
49 WaitEntry = WaitList->Flink;
50 }
51 }
52
53 VOID
54 FASTCALL
55 KiUnlinkThread(IN PKTHREAD Thread,
56 IN LONG_PTR WaitStatus)
57 {
58 PKWAIT_BLOCK WaitBlock;
59 PKTIMER Timer;
60
61 /* Update wait status */
62 Thread->WaitStatus |= WaitStatus;
63
64 /* Remove the Wait Blocks from the list */
65 WaitBlock = Thread->WaitBlockList;
66 do
67 {
68 /* Remove it */
69 RemoveEntryList(&WaitBlock->WaitListEntry);
70
71 /* Go to the next one */
72 WaitBlock = WaitBlock->NextWaitBlock;
73 } while (WaitBlock != Thread->WaitBlockList);
74
75 /* Remove the thread from the wait list! */
76 if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry);
77
78 /* Check if there's a Thread Timer */
79 Timer = &Thread->Timer;
80 if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
81
82 /* Increment the Queue's active threads */
83 if (Thread->Queue) Thread->Queue->CurrentCount++;
84 }
85
86 /* Must be called with the dispatcher lock held */
87 VOID
88 FASTCALL
89 KiUnwaitThread(IN PKTHREAD Thread,
90 IN LONG_PTR WaitStatus,
91 IN KPRIORITY Increment)
92 {
93 /* Unlink the thread */
94 KiUnlinkThread(Thread, WaitStatus);
95
96 /* Tell the scheduler do to the increment when it readies the thread */
97 ASSERT(Increment >= 0);
98 Thread->AdjustIncrement = (SCHAR)Increment;
99 Thread->AdjustReason = AdjustUnwait;
100
101 /* Reschedule the Thread */
102 KiReadyThread(Thread);
103 }
104
105 VOID
106 FASTCALL
107 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
108 {
109 /* Increase contention count */
110 FastMutex->Contention++;
111
112 /* Wait for the event */
113 KeWaitForSingleObject(&FastMutex->Event,
114 WrMutex,
115 KernelMode,
116 FALSE,
117 NULL);
118 }
119
120 VOID
121 FASTCALL
122 KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
123 {
124 ULONG BitsToRemove, BitsToAdd;
125 LONG OldValue, NewValue;
126
127 /* We depend on these bits being just right */
128 C_ASSERT((GM_LOCK_WAITER_WOKEN * 2) == GM_LOCK_WAITER_INC);
129
130 /* Increase the contention count */
131 GuardedMutex->Contention++;
132
133 /* Start by unlocking the Guarded Mutex */
134 BitsToRemove = GM_LOCK_BIT;
135 BitsToAdd = GM_LOCK_WAITER_INC;
136
137 /* Start change loop */
138 for (;;)
139 {
140 /* Loop sanity checks */
141 ASSERT((BitsToRemove == GM_LOCK_BIT) ||
142 (BitsToRemove == (GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN)));
143 ASSERT((BitsToAdd == GM_LOCK_WAITER_INC) ||
144 (BitsToAdd == GM_LOCK_WAITER_WOKEN));
145
146 /* Get the Count Bits */
147 OldValue = GuardedMutex->Count;
148
149 /* Start internal bit change loop */
150 for (;;)
151 {
152 /* Check if the Guarded Mutex is locked */
153 if (OldValue & GM_LOCK_BIT)
154 {
155 /* Sanity check */
156 ASSERT((BitsToRemove == GM_LOCK_BIT) ||
157 ((OldValue & GM_LOCK_WAITER_WOKEN) != 0));
158
159 /* Unlock it by removing the Lock Bit */
160 NewValue = OldValue ^ BitsToRemove;
161 NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
162 NewValue,
163 OldValue);
164 if (NewValue == OldValue) return;
165 }
166 else
167 {
168 /* The Guarded Mutex isn't locked, so simply set the bits */
169 NewValue = OldValue + BitsToAdd;
170 NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
171 NewValue,
172 OldValue);
173 if (NewValue == OldValue) break;
174 }
175
176 /* Old value changed, loop again */
177 OldValue = NewValue;
178 }
179
180 /* Now we have to wait for it */
181 KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode);
182 ASSERT((GuardedMutex->Count & GM_LOCK_WAITER_WOKEN) != 0);
183
184 /* Ok, the wait is done, so set the new bits */
185 BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN;
186 BitsToAdd = GM_LOCK_WAITER_WOKEN;
187 }
188 }
189
190 //
191 // This routine exits the dispatcher after a compatible operation and
192 // swaps the context to the next scheduled thread on the current CPU if
193 // one is available.
194 //
195 // It does NOT attempt to scan for a new thread to schedule.
196 //
197 VOID
198 FASTCALL
199 KiExitDispatcher(IN KIRQL OldIrql)
200 {
201 PKPRCB Prcb = KeGetCurrentPrcb();
202 PKTHREAD Thread, NextThread;
203 BOOLEAN PendingApc;
204
205 /* Make sure we're at synchronization level */
206 ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL);
207
208 /* Check if we have deferred threads */
209 KiCheckDeferredReadyList(Prcb);
210
211 /* Check if we were called at dispatcher level or higher */
212 if (OldIrql >= DISPATCH_LEVEL)
213 {
214 /* Check if we have a thread to schedule, and that no DPC is active */
215 if ((Prcb->NextThread) && !(Prcb->DpcRoutineActive))
216 {
217 /* Request DPC interrupt */
218 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
219 }
220
221 /* Lower IRQL and exit */
222 goto Quickie;
223 }
224
225 /* Make sure there's a new thread scheduled */
226 if (!Prcb->NextThread) goto Quickie;
227
228 /* Lock the PRCB */
229 KiAcquirePrcbLock(Prcb);
230
231 /* Get the next and current threads now */
232 NextThread = Prcb->NextThread;
233 Thread = Prcb->CurrentThread;
234
235 /* Set current thread's swap busy to true */
236 KiSetThreadSwapBusy(Thread);
237
238 /* Switch threads in PRCB */
239 Prcb->NextThread = NULL;
240 Prcb->CurrentThread = NextThread;
241
242 /* Set thread to running */
243 NextThread->State = Running;
244
245 /* Queue it on the ready lists */
246 KxQueueReadyThread(Thread, Prcb);
247
248 /* Set wait IRQL */
249 Thread->WaitIrql = OldIrql;
250
251 /* Swap threads and check if APCs were pending */
252 PendingApc = KiSwapContext(OldIrql, Thread);
253 if (PendingApc)
254 {
255 /* Lower only to APC */
256 KeLowerIrql(APC_LEVEL);
257
258 /* Deliver APCs */
259 KiDeliverApc(KernelMode, NULL, NULL);
260 ASSERT(OldIrql == PASSIVE_LEVEL);
261 }
262
263 /* Lower IRQl back */
264 Quickie:
265 KeLowerIrql(OldIrql);
266 }
267
268 /* PUBLIC FUNCTIONS **********************************************************/
269
270 BOOLEAN
271 NTAPI
272 KeIsWaitListEmpty(IN PVOID Object)
273 {
274 UNIMPLEMENTED;
275 return FALSE;
276 }
277
278 /*
279 * @implemented
280 */
281 NTSTATUS
282 NTAPI
283 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
284 IN BOOLEAN Alertable,
285 IN PLARGE_INTEGER Interval OPTIONAL)
286 {
287 PKTIMER Timer;
288 PKWAIT_BLOCK TimerBlock;
289 PKTHREAD Thread = KeGetCurrentThread();
290 NTSTATUS WaitStatus;
291 BOOLEAN Swappable;
292 PLARGE_INTEGER OriginalDueTime;
293 LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
294 ULONG Hand = 0;
295
296 if (Thread->WaitNext)
297 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
298 else
299 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
300
301 /* If this is a user-mode wait of 0 seconds, yield execution */
302 if (!(Interval->QuadPart) && (WaitMode != KernelMode))
303 {
304 /* Make sure the wait isn't alertable or interrupting an APC */
305 if (!(Alertable) && !(Thread->ApcState.UserApcPending))
306 {
307 /* Yield execution */
308 return NtYieldExecution();
309 }
310 }
311
312 /* Setup the original time and timer/wait blocks */
313 OriginalDueTime = Interval;
314 Timer = &Thread->Timer;
315 TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
316
317 /* Check if the lock is already held */
318 if (!Thread->WaitNext) goto WaitStart;
319
320 /* Otherwise, we already have the lock, so initialize the wait */
321 Thread->WaitNext = FALSE;
322 KxDelayThreadWait();
323
324 /* Start wait loop */
325 for (;;)
326 {
327 /* Disable pre-emption */
328 Thread->Preempted = FALSE;
329
330 /* Check if a kernel APC is pending and we're below APC_LEVEL */
331 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
332 (Thread->WaitIrql < APC_LEVEL))
333 {
334 /* Unlock the dispatcher */
335 KiReleaseDispatcherLock(Thread->WaitIrql);
336 }
337 else
338 {
339 /* Check if we have to bail out due to an alerted state */
340 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
341 if (WaitStatus != STATUS_WAIT_0) break;
342
343 /* Check if the timer expired */
344 InterruptTime.QuadPart = KeQueryInterruptTime();
345 if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart)
346 {
347 /* It did, so we don't need to wait */
348 goto NoWait;
349 }
350
351 /* It didn't, so activate it */
352 Timer->Header.Inserted = TRUE;
353
354 /* Handle Kernel Queues */
355 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
356
357 /* Setup the wait information */
358 Thread->State = Waiting;
359
360 /* Add the thread to the wait list */
361 KiAddThreadToWaitList(Thread, Swappable);
362
363 /* Insert the timer and swap the thread */
364 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
365 KiSetThreadSwapBusy(Thread);
366 KxInsertTimer(Timer, Hand);
367 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
368
369 /* Check if were swapped ok */
370 if (WaitStatus != STATUS_KERNEL_APC)
371 {
372 /* This is a good thing */
373 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
374
375 /* Return Status */
376 return WaitStatus;
377 }
378
379 /* Recalculate due times */
380 Interval = KiRecalculateDueTime(OriginalDueTime,
381 &DueTime,
382 &NewDueTime);
383 }
384
385 WaitStart:
386 /* Setup a new wait */
387 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
388 KxDelayThreadWait();
389 KiAcquireDispatcherLockAtDpcLevel();
390 }
391
392 /* We're done! */
393 KiReleaseDispatcherLock(Thread->WaitIrql);
394 return WaitStatus;
395
396 NoWait:
397 /* There was nothing to wait for. Did we have a wait interval? */
398 if (!Interval->QuadPart)
399 {
400 /* Unlock the dispatcher and do a yield */
401 KiReleaseDispatcherLock(Thread->WaitIrql);
402 return NtYieldExecution();
403 }
404
405 /* Unlock the dispatcher and adjust the quantum for a no-wait */
406 KiReleaseDispatcherLockFromDpcLevel();
407 KiAdjustQuantumThread(Thread);
408 return STATUS_SUCCESS;
409 }
410
411 /*
412 * @implemented
413 */
414 NTSTATUS
415 NTAPI
416 KeWaitForSingleObject(IN PVOID Object,
417 IN KWAIT_REASON WaitReason,
418 IN KPROCESSOR_MODE WaitMode,
419 IN BOOLEAN Alertable,
420 IN PLARGE_INTEGER Timeout OPTIONAL)
421 {
422 PKTHREAD Thread = KeGetCurrentThread();
423 PKMUTANT CurrentObject = (PKMUTANT)Object;
424 PKWAIT_BLOCK WaitBlock = &Thread->WaitBlock[0];
425 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
426 PKTIMER Timer = &Thread->Timer;
427 NTSTATUS WaitStatus;
428 BOOLEAN Swappable;
429 LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime;
430 PLARGE_INTEGER OriginalDueTime = Timeout;
431 ULONG Hand = 0;
432
433 if (Thread->WaitNext)
434 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
435 else
436 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL ||
437 (KeGetCurrentIrql() == DISPATCH_LEVEL &&
438 Timeout && Timeout->QuadPart == 0));
439
440 /* Check if the lock is already held */
441 if (!Thread->WaitNext) goto WaitStart;
442
443 /* Otherwise, we already have the lock, so initialize the wait */
444 Thread->WaitNext = FALSE;
445 KxSingleThreadWait();
446
447 /* Start wait loop */
448 for (;;)
449 {
450 /* Disable pre-emption */
451 Thread->Preempted = FALSE;
452
453 /* Check if a kernel APC is pending and we're below APC_LEVEL */
454 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
455 (Thread->WaitIrql < APC_LEVEL))
456 {
457 /* Unlock the dispatcher */
458 KiReleaseDispatcherLock(Thread->WaitIrql);
459 }
460 else
461 {
462 /* Sanity check */
463 ASSERT(CurrentObject->Header.Type != QueueObject);
464
465 /* Check if it's a mutant */
466 if (CurrentObject->Header.Type == MutantObject)
467 {
468 /* Check its signal state or if we own it */
469 if ((CurrentObject->Header.SignalState > 0) ||
470 (Thread == CurrentObject->OwnerThread))
471 {
472 /* Just unwait this guy and exit */
473 if (CurrentObject->Header.SignalState != (LONG)MINLONG)
474 {
475 /* It has a normal signal state. Unwait and return */
476 KiSatisfyMutantWait(CurrentObject, Thread);
477 WaitStatus = (NTSTATUS)Thread->WaitStatus;
478 goto DontWait;
479 }
480 else
481 {
482 /* Raise an exception */
483 KiReleaseDispatcherLock(Thread->WaitIrql);
484 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
485 }
486 }
487 }
488 else if (CurrentObject->Header.SignalState > 0)
489 {
490 /* Another satisfied object */
491 KiSatisfyNonMutantWait(CurrentObject);
492 WaitStatus = STATUS_WAIT_0;
493 goto DontWait;
494 }
495
496 /* Make sure we can satisfy the Alertable request */
497 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
498 if (WaitStatus != STATUS_WAIT_0) break;
499
500 /* Enable the Timeout Timer if there was any specified */
501 if (Timeout)
502 {
503 /* Check if the timer expired */
504 InterruptTime.QuadPart = KeQueryInterruptTime();
505 if ((ULONGLONG)InterruptTime.QuadPart >=
506 Timer->DueTime.QuadPart)
507 {
508 /* It did, so we don't need to wait */
509 WaitStatus = STATUS_TIMEOUT;
510 goto DontWait;
511 }
512
513 /* It didn't, so activate it */
514 Timer->Header.Inserted = TRUE;
515 }
516
517 /* Link the Object to this Wait Block */
518 InsertTailList(&CurrentObject->Header.WaitListHead,
519 &WaitBlock->WaitListEntry);
520
521 /* Handle Kernel Queues */
522 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
523
524 /* Setup the wait information */
525 Thread->State = Waiting;
526
527 /* Add the thread to the wait list */
528 KiAddThreadToWaitList(Thread, Swappable);
529
530 /* Activate thread swap */
531 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
532 KiSetThreadSwapBusy(Thread);
533
534 /* Check if we have a timer */
535 if (Timeout)
536 {
537 /* Insert it */
538 KxInsertTimer(Timer, Hand);
539 }
540 else
541 {
542 /* Otherwise, unlock the dispatcher */
543 KiReleaseDispatcherLockFromDpcLevel();
544 }
545
546 /* Do the actual swap */
547 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
548
549 /* Check if we were executing an APC */
550 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
551
552 /* Check if we had a timeout */
553 if (Timeout)
554 {
555 /* Recalculate due times */
556 Timeout = KiRecalculateDueTime(OriginalDueTime,
557 &DueTime,
558 &NewDueTime);
559 }
560 }
561 WaitStart:
562 /* Setup a new wait */
563 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
564 KxSingleThreadWait();
565 KiAcquireDispatcherLockAtDpcLevel();
566 }
567
568 /* Wait complete */
569 KiReleaseDispatcherLock(Thread->WaitIrql);
570 return WaitStatus;
571
572 DontWait:
573 /* Release dispatcher lock but maintain high IRQL */
574 KiReleaseDispatcherLockFromDpcLevel();
575
576 /* Adjust the Quantum and return the wait status */
577 KiAdjustQuantumThread(Thread);
578 return WaitStatus;
579 }
580
581 /*
582 * @implemented
583 */
584 NTSTATUS
585 NTAPI
586 KeWaitForMultipleObjects(IN ULONG Count,
587 IN PVOID Object[],
588 IN WAIT_TYPE WaitType,
589 IN KWAIT_REASON WaitReason,
590 IN KPROCESSOR_MODE WaitMode,
591 IN BOOLEAN Alertable,
592 IN PLARGE_INTEGER Timeout OPTIONAL,
593 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
594 {
595 PKMUTANT CurrentObject;
596 PKWAIT_BLOCK WaitBlock;
597 PKTHREAD Thread = KeGetCurrentThread();
598 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
599 PKTIMER Timer = &Thread->Timer;
600 NTSTATUS WaitStatus = STATUS_SUCCESS;
601 BOOLEAN Swappable;
602 PLARGE_INTEGER OriginalDueTime = Timeout;
603 LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime;
604 ULONG Index, Hand = 0;
605
606 if (Thread->WaitNext)
607 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
608 else if (KeGetCurrentIrql() == DISPATCH_LEVEL &&
609 (!Timeout || Timeout->QuadPart != 0))
610 {
611 /* HACK: tcpip is broken and waits with spinlocks acquired (CORE-6473) */
612 DPRINT("%s called at DISPATCH_LEVEL with non-zero timeout!\n",
613 __FUNCTION__);
614 }
615 else
616 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
617
618 /* Make sure the Wait Count is valid */
619 if (!WaitBlockArray)
620 {
621 /* Check in regards to the Thread Object Limit */
622 if (Count > THREAD_WAIT_OBJECTS)
623 {
624 /* Bugcheck */
625 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
626 }
627
628 /* Use the Thread's Wait Block */
629 WaitBlockArray = &Thread->WaitBlock[0];
630 }
631 else
632 {
633 /* Using our own Block Array, so check with the System Object Limit */
634 if (Count > MAXIMUM_WAIT_OBJECTS)
635 {
636 /* Bugcheck */
637 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
638 }
639 }
640
641 /* Sanity check */
642 ASSERT(Count != 0);
643
644 /* Check if the lock is already held */
645 if (!Thread->WaitNext) goto WaitStart;
646
647 /* Otherwise, we already have the lock, so initialize the wait */
648 Thread->WaitNext = FALSE;
649 /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */
650 /* uses (and modifies some of) the following local */
651 /* variables: */
652 /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */
653 /* If it looks like this code doesn't actually wait for any objects */
654 /* at all, it's because the setup is done by that macro. */
655 KxMultiThreadWait();
656
657 /* Start wait loop */
658 for (;;)
659 {
660 /* Disable pre-emption */
661 Thread->Preempted = FALSE;
662
663 /* Check if a kernel APC is pending and we're below APC_LEVEL */
664 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
665 (Thread->WaitIrql < APC_LEVEL))
666 {
667 /* Unlock the dispatcher */
668 KiReleaseDispatcherLock(Thread->WaitIrql);
669 }
670 else
671 {
672 /* Check what kind of wait this is */
673 Index = 0;
674 if (WaitType == WaitAny)
675 {
676 /* Loop blocks */
677 do
678 {
679 /* Get the Current Object */
680 CurrentObject = (PKMUTANT)Object[Index];
681 ASSERT(CurrentObject->Header.Type != QueueObject);
682
683 /* Check if the Object is a mutant */
684 if (CurrentObject->Header.Type == MutantObject)
685 {
686 /* Check if it's signaled */
687 if ((CurrentObject->Header.SignalState > 0) ||
688 (Thread == CurrentObject->OwnerThread))
689 {
690 /* This is a Wait Any, so unwait this and exit */
691 if (CurrentObject->Header.SignalState !=
692 (LONG)MINLONG)
693 {
694 /* Normal signal state, unwait it and return */
695 KiSatisfyMutantWait(CurrentObject, Thread);
696 WaitStatus = (NTSTATUS)Thread->WaitStatus | Index;
697 goto DontWait;
698 }
699 else
700 {
701 /* Raise an exception (see wasm.ru) */
702 KiReleaseDispatcherLock(Thread->WaitIrql);
703 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
704 }
705 }
706 }
707 else if (CurrentObject->Header.SignalState > 0)
708 {
709 /* Another signaled object, unwait and return */
710 KiSatisfyNonMutantWait(CurrentObject);
711 WaitStatus = Index;
712 goto DontWait;
713 }
714
715 /* Go to the next block */
716 Index++;
717 } while (Index < Count);
718 }
719 else
720 {
721 /* Loop blocks */
722 do
723 {
724 /* Get the Current Object */
725 CurrentObject = (PKMUTANT)Object[Index];
726 ASSERT(CurrentObject->Header.Type != QueueObject);
727
728 /* Check if we're dealing with a mutant again */
729 if (CurrentObject->Header.Type == MutantObject)
730 {
731 /* Check if it has an invalid count */
732 if ((Thread == CurrentObject->OwnerThread) &&
733 (CurrentObject->Header.SignalState == (LONG)MINLONG))
734 {
735 /* Raise an exception */
736 KiReleaseDispatcherLock(Thread->WaitIrql);
737 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
738 }
739 else if ((CurrentObject->Header.SignalState <= 0) &&
740 (Thread != CurrentObject->OwnerThread))
741 {
742 /* We don't own it, can't satisfy the wait */
743 break;
744 }
745 }
746 else if (CurrentObject->Header.SignalState <= 0)
747 {
748 /* Not signaled, can't satisfy */
749 break;
750 }
751
752 /* Go to the next block */
753 Index++;
754 } while (Index < Count);
755
756 /* Check if we've went through all the objects */
757 if (Index == Count)
758 {
759 /* Loop wait blocks */
760 WaitBlock = WaitBlockArray;
761 do
762 {
763 /* Get the object and satisfy it */
764 CurrentObject = (PKMUTANT)WaitBlock->Object;
765 KiSatisfyObjectWait(CurrentObject, Thread);
766
767 /* Go to the next block */
768 WaitBlock = WaitBlock->NextWaitBlock;
769 } while(WaitBlock != WaitBlockArray);
770
771 /* Set the wait status and get out */
772 WaitStatus = (NTSTATUS)Thread->WaitStatus;
773 goto DontWait;
774 }
775 }
776
777 /* Make sure we can satisfy the Alertable request */
778 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
779 if (WaitStatus != STATUS_WAIT_0) break;
780
781 /* Enable the Timeout Timer if there was any specified */
782 if (Timeout)
783 {
784 /* Check if the timer expired */
785 InterruptTime.QuadPart = KeQueryInterruptTime();
786 if ((ULONGLONG)InterruptTime.QuadPart >=
787 Timer->DueTime.QuadPart)
788 {
789 /* It did, so we don't need to wait */
790 WaitStatus = STATUS_TIMEOUT;
791 goto DontWait;
792 }
793
794 /* It didn't, so activate it */
795 Timer->Header.Inserted = TRUE;
796
797 /* Link the wait blocks */
798 WaitBlock->NextWaitBlock = TimerBlock;
799 }
800
801 /* Insert into Object's Wait List*/
802 WaitBlock = WaitBlockArray;
803 do
804 {
805 /* Get the Current Object */
806 CurrentObject = WaitBlock->Object;
807
808 /* Link the Object to this Wait Block */
809 InsertTailList(&CurrentObject->Header.WaitListHead,
810 &WaitBlock->WaitListEntry);
811
812 /* Move to the next Wait Block */
813 WaitBlock = WaitBlock->NextWaitBlock;
814 } while (WaitBlock != WaitBlockArray);
815
816 /* Handle Kernel Queues */
817 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
818
819 /* Setup the wait information */
820 Thread->State = Waiting;
821
822 /* Add the thread to the wait list */
823 KiAddThreadToWaitList(Thread, Swappable);
824
825 /* Activate thread swap */
826 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
827 KiSetThreadSwapBusy(Thread);
828
829 /* Check if we have a timer */
830 if (Timeout)
831 {
832 /* Insert it */
833 KxInsertTimer(Timer, Hand);
834 }
835 else
836 {
837 /* Otherwise, unlock the dispatcher */
838 KiReleaseDispatcherLockFromDpcLevel();
839 }
840
841 /* Swap the thread */
842 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
843
844 /* Check if we were executing an APC */
845 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
846
847 /* Check if we had a timeout */
848 if (Timeout)
849 {
850 /* Recalculate due times */
851 Timeout = KiRecalculateDueTime(OriginalDueTime,
852 &DueTime,
853 &NewDueTime);
854 }
855 }
856
857 WaitStart:
858 /* Setup a new wait */
859 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
860 KxMultiThreadWait();
861 KiAcquireDispatcherLockAtDpcLevel();
862 }
863
864 /* We are done */
865 KiReleaseDispatcherLock(Thread->WaitIrql);
866 return WaitStatus;
867
868 DontWait:
869 /* Release dispatcher lock but maintain high IRQL */
870 KiReleaseDispatcherLockFromDpcLevel();
871
872 /* Adjust the Quantum and return the wait status */
873 KiAdjustQuantumThread(Thread);
874 return WaitStatus;
875 }
876
877 NTSTATUS
878 NTAPI
879 NtDelayExecution(IN BOOLEAN Alertable,
880 IN PLARGE_INTEGER DelayInterval)
881 {
882 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
883 LARGE_INTEGER SafeInterval;
884 NTSTATUS Status;
885
886 /* Check the previous mode */
887 if (PreviousMode != KernelMode)
888 {
889 /* Enter SEH for probing */
890 _SEH2_TRY
891 {
892 /* Probe and capture the time out */
893 SafeInterval = ProbeForReadLargeInteger(DelayInterval);
894 DelayInterval = &SafeInterval;
895 }
896 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
897 {
898 /* Return the exception code */
899 _SEH2_YIELD(return _SEH2_GetExceptionCode());
900 }
901 _SEH2_END;
902 }
903
904 /* Call the Kernel Function */
905 Status = KeDelayExecutionThread(PreviousMode,
906 Alertable,
907 DelayInterval);
908
909 /* Return Status */
910 return Status;
911 }
912
913 /* EOF */