xref: /reactos/ntoskrnl/ke/wait.c (revision 1a83762c)
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         KiAcquireDispatcherLockAtSynchLevel();
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     KiReleaseDispatcherLockFromSynchLevel();
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() == SYNCH_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                 KiReleaseDispatcherLockFromSynchLevel();
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         KiAcquireDispatcherLockAtSynchLevel();
566     }
567 
568     /* Wait complete */
569     KiReleaseDispatcherLock(Thread->WaitIrql);
570     return WaitStatus;
571 
572 DontWait:
573     /* Release dispatcher lock but maintain high IRQL */
574     KiReleaseDispatcherLockFromSynchLevel();
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() == SYNCH_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                 KiReleaseDispatcherLockFromSynchLevel();
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         KiAcquireDispatcherLockAtSynchLevel();
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     KiReleaseDispatcherLockFromSynchLevel();
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 */
914