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)
10 /* INCLUDES ******************************************************************/
16 /* PRIVATE FUNCTIONS *********************************************************/
20 KiWaitTest(IN PVOID ObjectPointer
,
21 IN KPRIORITY Increment
)
23 PLIST_ENTRY WaitEntry
, WaitList
;
24 PKWAIT_BLOCK WaitBlock
;
26 PKMUTANT FirstObject
= ObjectPointer
;
29 /* Loop the Wait Entries */
30 WaitList
= &FirstObject
->Header
.WaitListHead
;
31 WaitEntry
= WaitList
->Flink
;
32 while ((FirstObject
->Header
.SignalState
> 0) && (WaitEntry
!= WaitList
))
34 /* Get the current wait block */
35 WaitBlock
= CONTAINING_RECORD(WaitEntry
, KWAIT_BLOCK
, WaitListEntry
);
36 WaitThread
= WaitBlock
->Thread
;
37 WaitStatus
= STATUS_KERNEL_APC
;
39 /* Check the current Wait Mode */
40 if (WaitBlock
->WaitType
== WaitAny
)
42 /* Easy case, satisfy only this wait */
43 WaitStatus
= (NTSTATUS
)WaitBlock
->WaitKey
;
44 KiSatisfyObjectWait(FirstObject
, WaitThread
);
47 /* Now do the rest of the unwait */
48 KiUnwaitThread(WaitThread
, WaitStatus
, Increment
);
49 WaitEntry
= WaitList
->Flink
;
55 KiUnlinkThread(IN PKTHREAD Thread
,
56 IN LONG_PTR WaitStatus
)
58 PKWAIT_BLOCK WaitBlock
;
61 /* Update wait status */
62 Thread
->WaitStatus
|= WaitStatus
;
64 /* Remove the Wait Blocks from the list */
65 WaitBlock
= Thread
->WaitBlockList
;
69 RemoveEntryList(&WaitBlock
->WaitListEntry
);
71 /* Go to the next one */
72 WaitBlock
= WaitBlock
->NextWaitBlock
;
73 } while (WaitBlock
!= Thread
->WaitBlockList
);
75 /* Remove the thread from the wait list! */
76 if (Thread
->WaitListEntry
.Flink
) RemoveEntryList(&Thread
->WaitListEntry
);
78 /* Check if there's a Thread Timer */
79 Timer
= &Thread
->Timer
;
80 if (Timer
->Header
.Inserted
) KxRemoveTreeTimer(Timer
);
82 /* Increment the Queue's active threads */
83 if (Thread
->Queue
) Thread
->Queue
->CurrentCount
++;
86 /* Must be called with the dispatcher lock held */
89 KiUnwaitThread(IN PKTHREAD Thread
,
90 IN LONG_PTR WaitStatus
,
91 IN KPRIORITY Increment
)
93 /* Unlink the thread */
94 KiUnlinkThread(Thread
, WaitStatus
);
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
;
101 /* Reschedule the Thread */
102 KiReadyThread(Thread
);
107 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex
)
109 /* Increase contention count */
110 FastMutex
->Contention
++;
112 /* Wait for the event */
113 KeWaitForSingleObject(&FastMutex
->Event
,
122 KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex
)
124 ULONG BitsToRemove
, BitsToAdd
;
125 LONG OldValue
, NewValue
;
127 /* We depend on these bits being just right */
128 C_ASSERT((GM_LOCK_WAITER_WOKEN
* 2) == GM_LOCK_WAITER_INC
);
130 /* Increase the contention count */
131 GuardedMutex
->Contention
++;
133 /* Start by unlocking the Guarded Mutex */
134 BitsToRemove
= GM_LOCK_BIT
;
135 BitsToAdd
= GM_LOCK_WAITER_INC
;
137 /* Start change loop */
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
));
146 /* Get the Count Bits */
147 OldValue
= GuardedMutex
->Count
;
149 /* Start internal bit change loop */
152 /* Check if the Guarded Mutex is locked */
153 if (OldValue
& GM_LOCK_BIT
)
156 ASSERT((BitsToRemove
== GM_LOCK_BIT
) ||
157 ((OldValue
& GM_LOCK_WAITER_WOKEN
) != 0));
159 /* Unlock it by removing the Lock Bit */
160 NewValue
= OldValue
^ BitsToRemove
;
161 NewValue
= InterlockedCompareExchange(&GuardedMutex
->Count
,
164 if (NewValue
== OldValue
) return;
168 /* The Guarded Mutex isn't locked, so simply set the bits */
169 NewValue
= OldValue
+ BitsToAdd
;
170 NewValue
= InterlockedCompareExchange(&GuardedMutex
->Count
,
173 if (NewValue
== OldValue
) break;
176 /* Old value changed, loop again */
180 /* Now we have to wait for it */
181 KeWaitForGate(&GuardedMutex
->Gate
, WrGuardedMutex
, KernelMode
);
182 ASSERT((GuardedMutex
->Count
& GM_LOCK_WAITER_WOKEN
) != 0);
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
;
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
195 // It does NOT attempt to scan for a new thread to schedule.
199 KiExitDispatcher(IN KIRQL OldIrql
)
201 PKPRCB Prcb
= KeGetCurrentPrcb();
202 PKTHREAD Thread
, NextThread
;
205 /* Make sure we're at synchronization level */
206 ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL
);
208 /* Check if we have deferred threads */
209 KiCheckDeferredReadyList(Prcb
);
211 /* Check if we were called at dispatcher level or higher */
212 if (OldIrql
>= DISPATCH_LEVEL
)
214 /* Check if we have a thread to schedule, and that no DPC is active */
215 if ((Prcb
->NextThread
) && !(Prcb
->DpcRoutineActive
))
217 /* Request DPC interrupt */
218 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
221 /* Lower IRQL and exit */
225 /* Make sure there's a new thread scheduled */
226 if (!Prcb
->NextThread
) goto Quickie
;
229 KiAcquirePrcbLock(Prcb
);
231 /* Get the next and current threads now */
232 NextThread
= Prcb
->NextThread
;
233 Thread
= Prcb
->CurrentThread
;
235 /* Set current thread's swap busy to true */
236 KiSetThreadSwapBusy(Thread
);
238 /* Switch threads in PRCB */
239 Prcb
->NextThread
= NULL
;
240 Prcb
->CurrentThread
= NextThread
;
242 /* Set thread to running */
243 NextThread
->State
= Running
;
245 /* Queue it on the ready lists */
246 KxQueueReadyThread(Thread
, Prcb
);
249 Thread
->WaitIrql
= OldIrql
;
251 /* Swap threads and check if APCs were pending */
252 PendingApc
= KiSwapContext(OldIrql
, Thread
);
255 /* Lower only to APC */
256 KeLowerIrql(APC_LEVEL
);
259 KiDeliverApc(KernelMode
, NULL
, NULL
);
260 ASSERT(OldIrql
== PASSIVE_LEVEL
);
263 /* Lower IRQl back */
265 KeLowerIrql(OldIrql
);
268 /* PUBLIC FUNCTIONS **********************************************************/
272 KeIsWaitListEmpty(IN PVOID Object
)
283 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode
,
284 IN BOOLEAN Alertable
,
285 IN PLARGE_INTEGER Interval OPTIONAL
)
288 PKWAIT_BLOCK TimerBlock
;
289 PKTHREAD Thread
= KeGetCurrentThread();
292 PLARGE_INTEGER OriginalDueTime
;
293 LARGE_INTEGER DueTime
, NewDueTime
, InterruptTime
;
296 if (Thread
->WaitNext
)
297 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
299 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
301 /* If this is a user-mode wait of 0 seconds, yield execution */
302 if (!(Interval
->QuadPart
) && (WaitMode
!= KernelMode
))
304 /* Make sure the wait isn't alertable or interrupting an APC */
305 if (!(Alertable
) && !(Thread
->ApcState
.UserApcPending
))
307 /* Yield execution */
308 return NtYieldExecution();
312 /* Setup the original time and timer/wait blocks */
313 OriginalDueTime
= Interval
;
314 Timer
= &Thread
->Timer
;
315 TimerBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
317 /* Check if the lock is already held */
318 if (!Thread
->WaitNext
) goto WaitStart
;
320 /* Otherwise, we already have the lock, so initialize the wait */
321 Thread
->WaitNext
= FALSE
;
324 /* Start wait loop */
327 /* Disable pre-emption */
328 Thread
->Preempted
= FALSE
;
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
))
334 /* Unlock the dispatcher */
335 KiReleaseDispatcherLock(Thread
->WaitIrql
);
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;
343 /* Check if the timer expired */
344 InterruptTime
.QuadPart
= KeQueryInterruptTime();
345 if ((ULONGLONG
)InterruptTime
.QuadPart
>= Timer
->DueTime
.QuadPart
)
347 /* It did, so we don't need to wait */
351 /* It didn't, so activate it */
352 Timer
->Header
.Inserted
= TRUE
;
354 /* Handle Kernel Queues */
355 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
357 /* Setup the wait information */
358 Thread
->State
= Waiting
;
360 /* Add the thread to the wait list */
361 KiAddThreadToWaitList(Thread
, Swappable
);
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());
369 /* Check if were swapped ok */
370 if (WaitStatus
!= STATUS_KERNEL_APC
)
372 /* This is a good thing */
373 if (WaitStatus
== STATUS_TIMEOUT
) WaitStatus
= STATUS_SUCCESS
;
379 /* Recalculate due times */
380 Interval
= KiRecalculateDueTime(OriginalDueTime
,
386 /* Setup a new wait */
387 Thread
->WaitIrql
= KeRaiseIrqlToSynchLevel();
389 KiAcquireDispatcherLockAtDpcLevel();
393 KiReleaseDispatcherLock(Thread
->WaitIrql
);
397 /* There was nothing to wait for. Did we have a wait interval? */
398 if (!Interval
->QuadPart
)
400 /* Unlock the dispatcher and do a yield */
401 KiReleaseDispatcherLock(Thread
->WaitIrql
);
402 return NtYieldExecution();
405 /* Unlock the dispatcher and adjust the quantum for a no-wait */
406 KiReleaseDispatcherLockFromDpcLevel();
407 KiAdjustQuantumThread(Thread
);
408 return STATUS_SUCCESS
;
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
)
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
;
429 LARGE_INTEGER DueTime
= {{0}}, NewDueTime
, InterruptTime
;
430 PLARGE_INTEGER OriginalDueTime
= Timeout
;
433 if (Thread
->WaitNext
)
434 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
436 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
||
437 (KeGetCurrentIrql() == DISPATCH_LEVEL
&&
438 Timeout
&& Timeout
->QuadPart
== 0));
440 /* Check if the lock is already held */
441 if (!Thread
->WaitNext
) goto WaitStart
;
443 /* Otherwise, we already have the lock, so initialize the wait */
444 Thread
->WaitNext
= FALSE
;
445 KxSingleThreadWait();
447 /* Start wait loop */
450 /* Disable pre-emption */
451 Thread
->Preempted
= FALSE
;
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
))
457 /* Unlock the dispatcher */
458 KiReleaseDispatcherLock(Thread
->WaitIrql
);
463 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
465 /* Check if it's a mutant */
466 if (CurrentObject
->Header
.Type
== MutantObject
)
468 /* Check its signal state or if we own it */
469 if ((CurrentObject
->Header
.SignalState
> 0) ||
470 (Thread
== CurrentObject
->OwnerThread
))
472 /* Just unwait this guy and exit */
473 if (CurrentObject
->Header
.SignalState
!= (LONG
)MINLONG
)
475 /* It has a normal signal state. Unwait and return */
476 KiSatisfyMutantWait(CurrentObject
, Thread
);
477 WaitStatus
= (NTSTATUS
)Thread
->WaitStatus
;
482 /* Raise an exception */
483 KiReleaseDispatcherLock(Thread
->WaitIrql
);
484 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
488 else if (CurrentObject
->Header
.SignalState
> 0)
490 /* Another satisfied object */
491 KiSatisfyNonMutantWait(CurrentObject
);
492 WaitStatus
= STATUS_WAIT_0
;
496 /* Make sure we can satisfy the Alertable request */
497 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
498 if (WaitStatus
!= STATUS_WAIT_0
) break;
500 /* Enable the Timeout Timer if there was any specified */
503 /* Check if the timer expired */
504 InterruptTime
.QuadPart
= KeQueryInterruptTime();
505 if ((ULONGLONG
)InterruptTime
.QuadPart
>=
506 Timer
->DueTime
.QuadPart
)
508 /* It did, so we don't need to wait */
509 WaitStatus
= STATUS_TIMEOUT
;
513 /* It didn't, so activate it */
514 Timer
->Header
.Inserted
= TRUE
;
517 /* Link the Object to this Wait Block */
518 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
519 &WaitBlock
->WaitListEntry
);
521 /* Handle Kernel Queues */
522 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
524 /* Setup the wait information */
525 Thread
->State
= Waiting
;
527 /* Add the thread to the wait list */
528 KiAddThreadToWaitList(Thread
, Swappable
);
530 /* Activate thread swap */
531 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
532 KiSetThreadSwapBusy(Thread
);
534 /* Check if we have a timer */
538 KxInsertTimer(Timer
, Hand
);
542 /* Otherwise, unlock the dispatcher */
543 KiReleaseDispatcherLockFromDpcLevel();
546 /* Do the actual swap */
547 WaitStatus
= (NTSTATUS
)KiSwapThread(Thread
, KeGetCurrentPrcb());
549 /* Check if we were executing an APC */
550 if (WaitStatus
!= STATUS_KERNEL_APC
) return WaitStatus
;
552 /* Check if we had a timeout */
555 /* Recalculate due times */
556 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
562 /* Setup a new wait */
563 Thread
->WaitIrql
= KeRaiseIrqlToSynchLevel();
564 KxSingleThreadWait();
565 KiAcquireDispatcherLockAtDpcLevel();
569 KiReleaseDispatcherLock(Thread
->WaitIrql
);
573 /* Release dispatcher lock but maintain high IRQL */
574 KiReleaseDispatcherLockFromDpcLevel();
576 /* Adjust the Quantum and return the wait status */
577 KiAdjustQuantumThread(Thread
);
586 KeWaitForMultipleObjects(IN ULONG Count
,
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
)
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
;
602 PLARGE_INTEGER OriginalDueTime
= Timeout
;
603 LARGE_INTEGER DueTime
= {{0}}, NewDueTime
, InterruptTime
;
604 ULONG Index
, Hand
= 0;
606 if (Thread
->WaitNext
)
607 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
608 else if (KeGetCurrentIrql() == DISPATCH_LEVEL
&&
609 (!Timeout
|| Timeout
->QuadPart
!= 0))
611 /* HACK: tcpip is broken and waits with spinlocks acquired (CORE-6473) */
612 DPRINT("%s called at DISPATCH_LEVEL with non-zero timeout!\n",
616 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
618 /* Make sure the Wait Count is valid */
621 /* Check in regards to the Thread Object Limit */
622 if (Count
> THREAD_WAIT_OBJECTS
)
625 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
628 /* Use the Thread's Wait Block */
629 WaitBlockArray
= &Thread
->WaitBlock
[0];
633 /* Using our own Block Array, so check with the System Object Limit */
634 if (Count
> MAXIMUM_WAIT_OBJECTS
)
637 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
644 /* Check if the lock is already held */
645 if (!Thread
->WaitNext
) goto WaitStart
;
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 */
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. */
657 /* Start wait loop */
660 /* Disable pre-emption */
661 Thread
->Preempted
= FALSE
;
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
))
667 /* Unlock the dispatcher */
668 KiReleaseDispatcherLock(Thread
->WaitIrql
);
672 /* Check what kind of wait this is */
674 if (WaitType
== WaitAny
)
679 /* Get the Current Object */
680 CurrentObject
= (PKMUTANT
)Object
[Index
];
681 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
683 /* Check if the Object is a mutant */
684 if (CurrentObject
->Header
.Type
== MutantObject
)
686 /* Check if it's signaled */
687 if ((CurrentObject
->Header
.SignalState
> 0) ||
688 (Thread
== CurrentObject
->OwnerThread
))
690 /* This is a Wait Any, so unwait this and exit */
691 if (CurrentObject
->Header
.SignalState
!=
694 /* Normal signal state, unwait it and return */
695 KiSatisfyMutantWait(CurrentObject
, Thread
);
696 WaitStatus
= (NTSTATUS
)Thread
->WaitStatus
| Index
;
701 /* Raise an exception (see wasm.ru) */
702 KiReleaseDispatcherLock(Thread
->WaitIrql
);
703 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
707 else if (CurrentObject
->Header
.SignalState
> 0)
709 /* Another signaled object, unwait and return */
710 KiSatisfyNonMutantWait(CurrentObject
);
715 /* Go to the next block */
717 } while (Index
< Count
);
724 /* Get the Current Object */
725 CurrentObject
= (PKMUTANT
)Object
[Index
];
726 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
728 /* Check if we're dealing with a mutant again */
729 if (CurrentObject
->Header
.Type
== MutantObject
)
731 /* Check if it has an invalid count */
732 if ((Thread
== CurrentObject
->OwnerThread
) &&
733 (CurrentObject
->Header
.SignalState
== (LONG
)MINLONG
))
735 /* Raise an exception */
736 KiReleaseDispatcherLock(Thread
->WaitIrql
);
737 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
739 else if ((CurrentObject
->Header
.SignalState
<= 0) &&
740 (Thread
!= CurrentObject
->OwnerThread
))
742 /* We don't own it, can't satisfy the wait */
746 else if (CurrentObject
->Header
.SignalState
<= 0)
748 /* Not signaled, can't satisfy */
752 /* Go to the next block */
754 } while (Index
< Count
);
756 /* Check if we've went through all the objects */
759 /* Loop wait blocks */
760 WaitBlock
= WaitBlockArray
;
763 /* Get the object and satisfy it */
764 CurrentObject
= (PKMUTANT
)WaitBlock
->Object
;
765 KiSatisfyObjectWait(CurrentObject
, Thread
);
767 /* Go to the next block */
768 WaitBlock
= WaitBlock
->NextWaitBlock
;
769 } while(WaitBlock
!= WaitBlockArray
);
771 /* Set the wait status and get out */
772 WaitStatus
= (NTSTATUS
)Thread
->WaitStatus
;
777 /* Make sure we can satisfy the Alertable request */
778 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
779 if (WaitStatus
!= STATUS_WAIT_0
) break;
781 /* Enable the Timeout Timer if there was any specified */
784 /* Check if the timer expired */
785 InterruptTime
.QuadPart
= KeQueryInterruptTime();
786 if ((ULONGLONG
)InterruptTime
.QuadPart
>=
787 Timer
->DueTime
.QuadPart
)
789 /* It did, so we don't need to wait */
790 WaitStatus
= STATUS_TIMEOUT
;
794 /* It didn't, so activate it */
795 Timer
->Header
.Inserted
= TRUE
;
797 /* Link the wait blocks */
798 WaitBlock
->NextWaitBlock
= TimerBlock
;
801 /* Insert into Object's Wait List*/
802 WaitBlock
= WaitBlockArray
;
805 /* Get the Current Object */
806 CurrentObject
= WaitBlock
->Object
;
808 /* Link the Object to this Wait Block */
809 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
810 &WaitBlock
->WaitListEntry
);
812 /* Move to the next Wait Block */
813 WaitBlock
= WaitBlock
->NextWaitBlock
;
814 } while (WaitBlock
!= WaitBlockArray
);
816 /* Handle Kernel Queues */
817 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
819 /* Setup the wait information */
820 Thread
->State
= Waiting
;
822 /* Add the thread to the wait list */
823 KiAddThreadToWaitList(Thread
, Swappable
);
825 /* Activate thread swap */
826 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
827 KiSetThreadSwapBusy(Thread
);
829 /* Check if we have a timer */
833 KxInsertTimer(Timer
, Hand
);
837 /* Otherwise, unlock the dispatcher */
838 KiReleaseDispatcherLockFromDpcLevel();
841 /* Swap the thread */
842 WaitStatus
= (NTSTATUS
)KiSwapThread(Thread
, KeGetCurrentPrcb());
844 /* Check if we were executing an APC */
845 if (WaitStatus
!= STATUS_KERNEL_APC
) return WaitStatus
;
847 /* Check if we had a timeout */
850 /* Recalculate due times */
851 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
858 /* Setup a new wait */
859 Thread
->WaitIrql
= KeRaiseIrqlToSynchLevel();
861 KiAcquireDispatcherLockAtDpcLevel();
865 KiReleaseDispatcherLock(Thread
->WaitIrql
);
869 /* Release dispatcher lock but maintain high IRQL */
870 KiReleaseDispatcherLockFromDpcLevel();
872 /* Adjust the Quantum and return the wait status */
873 KiAdjustQuantumThread(Thread
);
879 NtDelayExecution(IN BOOLEAN Alertable
,
880 IN PLARGE_INTEGER DelayInterval
)
882 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
883 LARGE_INTEGER SafeInterval
;
886 /* Check the previous mode */
887 if (PreviousMode
!= KernelMode
)
889 /* Enter SEH for probing */
892 /* Probe and capture the time out */
893 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
894 DelayInterval
= &SafeInterval
;
896 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
898 /* Return the exception code */
899 _SEH2_YIELD(return _SEH2_GetExceptionCode());
904 /* Call the Kernel Function */
905 Status
= KeDelayExecutionThread(PreviousMode
,