xref: /reactos/ntoskrnl/ke/wait.c (revision 84344399)
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 != 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 (!Timeout || (Timeout->QuadPart != 0))
609     {
610         ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
611     }
612     else
613         ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
614 
615     /* Make sure the Wait Count is valid */
616     if (!WaitBlockArray)
617     {
618         /* Check in regards to the Thread Object Limit */
619         if (Count > THREAD_WAIT_OBJECTS)
620         {
621             /* Bugcheck */
622             KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
623         }
624 
625         /* Use the Thread's Wait Block */
626         WaitBlockArray = &Thread->WaitBlock[0];
627     }
628     else
629     {
630         /* Using our own Block Array, so check with the System Object Limit */
631         if (Count > MAXIMUM_WAIT_OBJECTS)
632         {
633             /* Bugcheck */
634             KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
635         }
636     }
637 
638     /* Sanity check */
639     ASSERT(Count != 0);
640 
641     /* Check if the lock is already held */
642     if (!Thread->WaitNext) goto WaitStart;
643 
644     /*  Otherwise, we already have the lock, so initialize the wait */
645     Thread->WaitNext = FALSE;
646     /*  Note that KxMultiThreadWait is a macro, defined in ke_x.h, that  */
647     /*  uses  (and modifies some of) the following local                 */
648     /*  variables:                                                       */
649     /*  Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable.    */
650     /*  If it looks like this code doesn't actually wait for any objects */
651     /*  at all, it's because the setup is done by that macro.            */
652     KxMultiThreadWait();
653 
654     /* Start wait loop */
655     for (;;)
656     {
657         /* Disable pre-emption */
658         Thread->Preempted = FALSE;
659 
660         /* Check if a kernel APC is pending and we're below APC_LEVEL */
661         if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
662             (Thread->WaitIrql < APC_LEVEL))
663         {
664             /* Unlock the dispatcher */
665             KiReleaseDispatcherLock(Thread->WaitIrql);
666         }
667         else
668         {
669             /* Check what kind of wait this is */
670             Index = 0;
671             if (WaitType == WaitAny)
672             {
673                 /* Loop blocks */
674                 do
675                 {
676                     /* Get the Current Object */
677                     CurrentObject = (PKMUTANT)Object[Index];
678                     ASSERT(CurrentObject->Header.Type != QueueObject);
679 
680                     /* Check if the Object is a mutant */
681                     if (CurrentObject->Header.Type == MutantObject)
682                     {
683                         /* Check if it's signaled */
684                         if ((CurrentObject->Header.SignalState > 0) ||
685                             (Thread == CurrentObject->OwnerThread))
686                         {
687                             /* This is a Wait Any, so unwait this and exit */
688                             if (CurrentObject->Header.SignalState !=
689                                 (LONG)MINLONG)
690                             {
691                                 /* Normal signal state, unwait it and return */
692                                 KiSatisfyMutantWait(CurrentObject, Thread);
693                                 WaitStatus = (NTSTATUS)Thread->WaitStatus | Index;
694                                 goto DontWait;
695                             }
696                             else
697                             {
698                                 /* Raise an exception (see wasm.ru) */
699                                 KiReleaseDispatcherLock(Thread->WaitIrql);
700                                 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
701                             }
702                         }
703                     }
704                     else if (CurrentObject->Header.SignalState > 0)
705                     {
706                         /* Another signaled object, unwait and return */
707                         KiSatisfyNonMutantWait(CurrentObject);
708                         WaitStatus = Index;
709                         goto DontWait;
710                     }
711 
712                     /* Go to the next block */
713                     Index++;
714                 } while (Index < Count);
715             }
716             else
717             {
718                 /* Loop blocks */
719                 do
720                 {
721                     /* Get the Current Object */
722                     CurrentObject = (PKMUTANT)Object[Index];
723                     ASSERT(CurrentObject->Header.Type != QueueObject);
724 
725                     /* Check if we're dealing with a mutant again */
726                     if (CurrentObject->Header.Type == MutantObject)
727                     {
728                         /* Check if it has an invalid count */
729                         if ((Thread == CurrentObject->OwnerThread) &&
730                             (CurrentObject->Header.SignalState == (LONG)MINLONG))
731                         {
732                             /* Raise an exception */
733                             KiReleaseDispatcherLock(Thread->WaitIrql);
734                             ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
735                         }
736                         else if ((CurrentObject->Header.SignalState <= 0) &&
737                                  (Thread != CurrentObject->OwnerThread))
738                         {
739                             /* We don't own it, can't satisfy the wait */
740                             break;
741                         }
742                     }
743                     else if (CurrentObject->Header.SignalState <= 0)
744                     {
745                         /* Not signaled, can't satisfy */
746                         break;
747                     }
748 
749                     /* Go to the next block */
750                     Index++;
751                 } while (Index < Count);
752 
753                 /* Check if we've went through all the objects */
754                 if (Index == Count)
755                 {
756                     /* Loop wait blocks */
757                     WaitBlock = WaitBlockArray;
758                     do
759                     {
760                         /* Get the object and satisfy it */
761                         CurrentObject = (PKMUTANT)WaitBlock->Object;
762                         KiSatisfyObjectWait(CurrentObject, Thread);
763 
764                         /* Go to the next block */
765                         WaitBlock = WaitBlock->NextWaitBlock;
766                     } while(WaitBlock != WaitBlockArray);
767 
768                     /* Set the wait status and get out */
769                     WaitStatus = (NTSTATUS)Thread->WaitStatus;
770                     goto DontWait;
771                 }
772             }
773 
774             /* Make sure we can satisfy the Alertable request */
775             WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
776             if (WaitStatus != STATUS_WAIT_0) break;
777 
778             /* Enable the Timeout Timer if there was any specified */
779             if (Timeout)
780             {
781                 /* Check if the timer expired */
782                 InterruptTime.QuadPart = KeQueryInterruptTime();
783                 if ((ULONGLONG)InterruptTime.QuadPart >=
784                     Timer->DueTime.QuadPart)
785                 {
786                     /* It did, so we don't need to wait */
787                     WaitStatus = STATUS_TIMEOUT;
788                     goto DontWait;
789                 }
790 
791                 /* It didn't, so activate it */
792                 Timer->Header.Inserted = TRUE;
793 
794                 /* Link the wait blocks */
795                 WaitBlock->NextWaitBlock = TimerBlock;
796             }
797 
798             /* Insert into Object's Wait List*/
799             WaitBlock = WaitBlockArray;
800             do
801             {
802                 /* Get the Current Object */
803                 CurrentObject = WaitBlock->Object;
804 
805                 /* Link the Object to this Wait Block */
806                 InsertTailList(&CurrentObject->Header.WaitListHead,
807                                &WaitBlock->WaitListEntry);
808 
809                 /* Move to the next Wait Block */
810                 WaitBlock = WaitBlock->NextWaitBlock;
811             } while (WaitBlock != WaitBlockArray);
812 
813             /* Handle Kernel Queues */
814             if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
815 
816             /* Setup the wait information */
817             Thread->State = Waiting;
818 
819             /* Add the thread to the wait list */
820             KiAddThreadToWaitList(Thread, Swappable);
821 
822             /* Activate thread swap */
823             ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
824             KiSetThreadSwapBusy(Thread);
825 
826             /* Check if we have a timer */
827             if (Timeout)
828             {
829                 /* Insert it */
830                 KxInsertTimer(Timer, Hand);
831             }
832             else
833             {
834                 /* Otherwise, unlock the dispatcher */
835                 KiReleaseDispatcherLockFromSynchLevel();
836             }
837 
838             /* Swap the thread */
839             WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
840 
841             /* Check if we were executing an APC */
842             if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
843 
844             /* Check if we had a timeout */
845             if (Timeout)
846             {
847                 /* Recalculate due times */
848                 Timeout = KiRecalculateDueTime(OriginalDueTime,
849                                                &DueTime,
850                                                &NewDueTime);
851             }
852         }
853 
854 WaitStart:
855         /* Setup a new wait */
856         Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
857         KxMultiThreadWait();
858         KiAcquireDispatcherLockAtSynchLevel();
859     }
860 
861     /* We are done */
862     KiReleaseDispatcherLock(Thread->WaitIrql);
863     return WaitStatus;
864 
865 DontWait:
866     /* Release dispatcher lock but maintain high IRQL */
867     KiReleaseDispatcherLockFromSynchLevel();
868 
869     /* Adjust the Quantum and return the wait status */
870     KiAdjustQuantumThread(Thread);
871     return WaitStatus;
872 }
873 
874 NTSTATUS
875 NTAPI
876 NtDelayExecution(IN BOOLEAN Alertable,
877                  IN PLARGE_INTEGER DelayInterval)
878 {
879     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
880     LARGE_INTEGER SafeInterval;
881     NTSTATUS Status;
882 
883     /* Check the previous mode */
884     if (PreviousMode != KernelMode)
885     {
886         /* Enter SEH for probing */
887         _SEH2_TRY
888         {
889             /* Probe and capture the time out */
890             SafeInterval = ProbeForReadLargeInteger(DelayInterval);
891             DelayInterval = &SafeInterval;
892         }
893         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
894         {
895             /* Return the exception code */
896             _SEH2_YIELD(return _SEH2_GetExceptionCode());
897         }
898         _SEH2_END;
899    }
900 
901    /* Call the Kernel Function */
902    Status = KeDelayExecutionThread(PreviousMode,
903                                    Alertable,
904                                    DelayInterval);
905 
906    /* Return Status */
907    return Status;
908 }
909 
910 /* EOF */
911