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 ******************************************************************/
14 #include <internal/debug.h>
16 /* PRIVATE FUNCTIONS *********************************************************/
20 KiWaitSatisfyAll(PKWAIT_BLOCK FirstBlock
)
22 PKWAIT_BLOCK WaitBlock
= FirstBlock
;
23 PKTHREAD WaitThread
= WaitBlock
->Thread
;
25 /* Loop through all the Wait Blocks, and wake each Object */
28 /* Make sure it hasn't timed out */
29 if (WaitBlock
->WaitKey
!= STATUS_TIMEOUT
)
32 KiSatisfyObjectWait((PKMUTANT
)WaitBlock
->Object
, WaitThread
);
35 /* Move to the next block */
36 WaitBlock
= WaitBlock
->NextWaitBlock
;
37 } while (WaitBlock
!= FirstBlock
);
42 KiWaitTest(IN PVOID ObjectPointer
,
43 IN KPRIORITY Increment
)
45 PLIST_ENTRY WaitEntry
;
47 PKWAIT_BLOCK CurrentWaitBlock
;
48 PKWAIT_BLOCK NextWaitBlock
;
50 PKMUTANT FirstObject
= ObjectPointer
, Object
;
52 /* Loop the Wait Entries */
53 WaitList
= &FirstObject
->Header
.WaitListHead
;
54 WaitEntry
= WaitList
->Flink
;
55 while ((FirstObject
->Header
.SignalState
> 0) &&
56 (WaitEntry
!= WaitList
))
58 /* Get the current wait block */
59 CurrentWaitBlock
= CONTAINING_RECORD(WaitEntry
,
62 WaitThread
= CurrentWaitBlock
->Thread
;
64 /* Check the current Wait Mode */
65 if (CurrentWaitBlock
->WaitType
== WaitAny
)
67 /* Easy case, satisfy only this wait */
68 WaitEntry
= WaitEntry
->Blink
;
69 KiSatisfyObjectWait(FirstObject
, WaitThread
);
73 /* Everything must be satisfied */
74 NextWaitBlock
= CurrentWaitBlock
->NextWaitBlock
;
76 /* Loop first to make sure they are valid */
77 while (NextWaitBlock
!= CurrentWaitBlock
)
79 /* Make sure this isn't a timeout block */
80 if (NextWaitBlock
->WaitKey
!= STATUS_TIMEOUT
)
83 Object
= NextWaitBlock
->Object
;
85 /* Check if this is a mutant */
86 if ((Object
->Header
.Type
== MutantObject
) &&
87 (Object
->Header
.SignalState
<= 0) &&
88 (WaitThread
== Object
->OwnerThread
))
90 /* It's a signaled mutant */
92 else if (Object
->Header
.SignalState
<= 0)
94 /* Skip the unwaiting */
99 /* Go to the next Wait block */
100 NextWaitBlock
= NextWaitBlock
->NextWaitBlock
;
103 /* All the objects are signaled, we can satisfy */
104 WaitEntry
= WaitEntry
->Blink
;
105 KiWaitSatisfyAll(CurrentWaitBlock
);
108 /* All waits satisfied, unwait the thread */
109 KiUnwaitThread(WaitThread
, CurrentWaitBlock
->WaitKey
, Increment
);
113 WaitEntry
= WaitEntry
->Flink
;
119 KiUnlinkThread(IN PKTHREAD Thread
,
120 IN NTSTATUS WaitStatus
)
122 PKWAIT_BLOCK WaitBlock
;
125 /* Update wait status */
126 Thread
->WaitStatus
|= WaitStatus
;
128 /* Remove the Wait Blocks from the list */
129 WaitBlock
= Thread
->WaitBlockList
;
133 RemoveEntryList(&WaitBlock
->WaitListEntry
);
135 /* Go to the next one */
136 WaitBlock
= WaitBlock
->NextWaitBlock
;
137 } while (WaitBlock
!= Thread
->WaitBlockList
);
139 /* Remove the thread from the wait list! */
140 if (Thread
->WaitListEntry
.Flink
) RemoveEntryList(&Thread
->WaitListEntry
);
142 /* Check if there's a Thread Timer */
143 Timer
= &Thread
->Timer
;
144 if (Timer
->Header
.Inserted
)
146 /* Remove the timer */
147 Timer
->Header
.Inserted
= FALSE
;
148 RemoveEntryList(&Timer
->TimerListEntry
);
149 //KiRemoveTimer(Timer);
152 /* Increment the Queue's active threads */
153 if (Thread
->Queue
) Thread
->Queue
->CurrentCount
++;
156 /* Must be called with the dispatcher lock held */
159 KiUnwaitThread(IN PKTHREAD Thread
,
160 IN NTSTATUS WaitStatus
,
161 IN KPRIORITY Increment
)
163 /* Unlink the thread */
164 KiUnlinkThread(Thread
, WaitStatus
);
166 /* Tell the scheduler do to the increment when it readies the thread */
167 ASSERT(Increment
>= 0);
168 Thread
->AdjustIncrement
= (SCHAR
)Increment
;
169 Thread
->AdjustReason
= 1;
171 /* Reschedule the Thread */
172 KiReadyThread(Thread
);
177 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex
)
179 /* Increase contention count */
180 FastMutex
->Contention
++;
182 /* Wait for the event */
183 KeWaitForSingleObject(&FastMutex
->Gate
,
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_IRQL(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
;
228 /* This shouldn't happen on ROS yet */
229 DPRINT1("The impossible happened - Tell Alex\n");
233 KiAcquirePrcbLock(Prcb
);
235 /* Get the next and current threads now */
236 NextThread
= Prcb
->NextThread
;
237 Thread
= Prcb
->CurrentThread
;
239 /* Set current thread's swap busy to true */
240 KiSetThreadSwapBusy(Thread
);
242 /* Switch threads in PRCB */
243 Prcb
->NextThread
= NULL
;
244 Prcb
->CurrentThread
= NextThread
;
246 /* Set thread to running */
247 NextThread
->State
= Running
;
249 /* Queue it on the ready lists */
250 KxQueueReadyThread(Thread
, Prcb
);
253 Thread
->WaitIrql
= OldIrql
;
255 /* Swap threads and check if APCs were pending */
256 PendingApc
= KiSwapContext(Thread
, NextThread
);
259 /* Lower only to APC */
260 KeLowerIrql(APC_LEVEL
);
263 KiDeliverApc(KernelMode
, NULL
, NULL
);
264 ASSERT(OldIrql
== PASSIVE_LEVEL
);
267 /* Lower IRQl back */
269 KeLowerIrql(OldIrql
);
272 /* PUBLIC FUNCTIONS **********************************************************/
279 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode
,
280 IN BOOLEAN Alertable
,
281 IN PLARGE_INTEGER Interval OPTIONAL
)
284 PKTHREAD Thread
= KeGetCurrentThread();
287 PLARGE_INTEGER OriginalDueTime
= Interval
;
288 LARGE_INTEGER DueTime
, NewDueTime
;
290 /* Check if the lock is already held */
291 if (Thread
->WaitNext
)
293 /* Lock is held, disable Wait Next */
294 Thread
->WaitNext
= FALSE
;
295 Swappable
= KxDelayThreadWait(Thread
, Alertable
, WaitMode
);
299 /* Lock not held, acquire it */
301 Thread
->WaitIrql
= KiAcquireDispatcherLock();
302 Swappable
= KxDelayThreadWait(Thread
, Alertable
, WaitMode
);
305 /* Check if a kernel APC is pending and we're below APC_LEVEL */
306 if ((Thread
->ApcState
.KernelApcPending
) && !(Thread
->SpecialApcDisable
) &&
307 (Thread
->WaitIrql
< APC_LEVEL
))
309 /* Unlock the dispatcher */
310 KiReleaseDispatcherLock(Thread
->WaitIrql
);
314 /* Check if we have to bail out due to an alerted state */
315 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
316 if (WaitStatus
!= STATUS_WAIT_0
)
318 /* Unlock the dispatcher and return */
319 KiReleaseDispatcherLock(Thread
->WaitIrql
);
324 ThreadTimer
= &Thread
->Timer
;
326 /* Insert the Timer into the Timer Lists and enable it */
327 if (!KiInsertTimer(ThreadTimer
, *Interval
))
329 /* FIXME: We should find a new ready thread */
330 KiReleaseDispatcherLock(Thread
->WaitIrql
);
331 return STATUS_WAIT_0
;
335 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
337 /* Handle Kernel Queues */
338 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
340 /* Setup the wait information */
341 Thread
->State
= Waiting
;
343 /* Add the thread to the wait list */
344 KiAddThreadToWaitList(Thread
, Swappable
);
346 /* Swap the thread */
347 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
348 KiSetThreadSwapBusy(Thread
);
349 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
351 /* Check if we were executing an APC or if we timed out */
352 if (WaitStatus
== STATUS_KERNEL_APC
)
354 /* Recalculate due times */
355 Interval
= KiRecalculateDueTime(OriginalDueTime
,
361 /* This is a good thing */
362 if (WaitStatus
== STATUS_TIMEOUT
) WaitStatus
= STATUS_SUCCESS
;
373 KeWaitForSingleObject(IN PVOID Object
,
374 IN KWAIT_REASON WaitReason
,
375 IN KPROCESSOR_MODE WaitMode
,
376 IN BOOLEAN Alertable
,
377 IN PLARGE_INTEGER Timeout OPTIONAL
)
379 PKMUTANT CurrentObject
;
380 PKWAIT_BLOCK WaitBlock
;
382 PKTHREAD Thread
= KeGetCurrentThread();
385 LARGE_INTEGER DueTime
, NewDueTime
;
386 PLARGE_INTEGER OriginalDueTime
= Timeout
;
389 WaitBlock
= &Thread
->WaitBlock
[0];
391 /* Check if the lock is already held */
392 if (Thread
->WaitNext
)
394 /* Lock is held, disable Wait Next */
395 Thread
->WaitNext
= FALSE
;
396 Swappable
= KxSingleThreadWait(Thread
,
407 /* Lock not held, acquire it */
408 Thread
->WaitIrql
= KiAcquireDispatcherLock();
409 Swappable
= KxSingleThreadWait(Thread
,
418 /* Check if a kernel APC is pending and we're below APC_LEVEL */
419 if ((Thread
->ApcState
.KernelApcPending
) && !(Thread
->SpecialApcDisable
) &&
420 (Thread
->WaitIrql
< APC_LEVEL
))
422 /* Unlock the dispatcher and wait again */
423 KiReleaseDispatcherLock(Thread
->WaitIrql
);
427 /* Get the Current Object */
428 CurrentObject
= (PKMUTANT
)Object
;
429 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
431 /* Check if it's a mutant */
432 if (CurrentObject
->Header
.Type
== MutantObject
)
434 /* Check its signal state or if we own it */
435 if ((CurrentObject
->Header
.SignalState
> 0) ||
436 (Thread
== CurrentObject
->OwnerThread
))
438 /* Just unwait this guy and exit */
439 if (CurrentObject
->Header
.SignalState
!= (LONG
)MINLONG
)
441 /* It has a normal signal state. Unwait and return */
442 KiSatisfyMutantWait(CurrentObject
, Thread
);
443 WaitStatus
= Thread
->WaitStatus
;
448 /* Raise an exception */
449 KiReleaseDispatcherLock(Thread
->WaitIrql
);
450 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
454 else if (CurrentObject
->Header
.SignalState
> 0)
456 /* Another satisfied object */
457 KiSatisfyNonMutantWait(CurrentObject
, Thread
);
458 WaitStatus
= STATUS_WAIT_0
;
462 /* Make sure we can satisfy the Alertable request */
463 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
464 if (WaitStatus
!= STATUS_WAIT_0
)
466 /* Unlock the dispatcher and return */
467 KiReleaseDispatcherLock(Thread
->WaitIrql
);
471 /* Enable the Timeout Timer if there was any specified */
474 /* Fail if the timeout interval is actually 0 */
475 if (!Timeout
->QuadPart
)
477 /* Return a timeout */
478 WaitStatus
= STATUS_TIMEOUT
;
482 /* Insert the Timer into the Timer Lists and enable it */
483 ThreadTimer
= &Thread
->Timer
;
484 if (!KiInsertTimer(ThreadTimer
, *Timeout
))
486 /* Return a timeout if we couldn't insert the timer */
487 WaitStatus
= STATUS_TIMEOUT
;
491 /* Set the current due time */
492 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
495 /* Link the Object to this Wait Block */
496 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
497 &WaitBlock
->WaitListEntry
);
499 /* Handle Kernel Queues */
500 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
502 /* Setup the wait information */
503 Thread
->State
= Waiting
;
505 /* Add the thread to the wait list */
506 KiAddThreadToWaitList(Thread
, Swappable
);
508 /* Swap the thread */
509 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
510 KiSetThreadSwapBusy(Thread
);
511 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
513 /* Check if we were executing an APC */
514 if (WaitStatus
== STATUS_KERNEL_APC
)
516 /* Check if we had a timeout */
519 /* Recalculate due times */
520 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
533 /* Adjust the Quantum */
534 KiAdjustQuantumThread(Thread
);
536 /* Release & Return */
537 KiReleaseDispatcherLock(Thread
->WaitIrql
);
546 KeWaitForMultipleObjects(IN ULONG Count
,
548 IN WAIT_TYPE WaitType
,
549 IN KWAIT_REASON WaitReason
,
550 IN KPROCESSOR_MODE WaitMode
,
551 IN BOOLEAN Alertable
,
552 IN PLARGE_INTEGER Timeout OPTIONAL
,
553 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL
)
555 PKMUTANT CurrentObject
;
556 PKWAIT_BLOCK WaitBlock
;
557 PKWAIT_BLOCK TimerWaitBlock
;
559 PKTHREAD Thread
= KeGetCurrentThread();
560 ULONG AllObjectsSignaled
;
562 NTSTATUS WaitStatus
= STATUS_SUCCESS
;
564 PLARGE_INTEGER OriginalDueTime
= Timeout
;
565 LARGE_INTEGER DueTime
, NewDueTime
;
567 /* Make sure the Wait Count is valid */
570 /* Check in regards to the Thread Object Limit */
571 if (Count
> THREAD_WAIT_OBJECTS
)
574 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
577 /* Use the Thread's Wait Block */
578 WaitBlockArray
= &Thread
->WaitBlock
[0];
582 /* Using our own Block Array, so check with the System Object Limit */
583 if (Count
> MAXIMUM_WAIT_OBJECTS
)
586 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
593 /* Check if the lock is already held */
594 if (Thread
->WaitNext
)
596 /* Lock is held, disable Wait Next */
597 Thread
->WaitNext
= FALSE
;
601 /* Lock not held, acquire it */
603 Thread
->WaitIrql
= KiAcquireDispatcherLock();
606 /* Prepare for the wait */
607 Swappable
= KxMultiThreadWait(Thread
,
613 /* Check if a kernel APC is pending and we're below APC_LEVEL */
614 if ((Thread
->ApcState
.KernelApcPending
) && !(Thread
->SpecialApcDisable
) &&
615 (Thread
->WaitIrql
< APC_LEVEL
))
617 /* Unlock the dispatcher */
618 KiReleaseDispatcherLock(Thread
->WaitIrql
);
622 /* Append wait block to the KTHREAD wait block list */
623 WaitBlock
= WaitBlockArray
;
625 /* Check if the wait is (already) satisfied */
626 AllObjectsSignaled
= TRUE
;
628 /* First, we'll try to satisfy the wait directly */
629 for (WaitIndex
= 0; WaitIndex
< Count
; WaitIndex
++)
631 /* Get the Current Object */
632 CurrentObject
= (PKMUTANT
)Object
[WaitIndex
];
633 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
635 /* Check the type of wait */
636 if (WaitType
== WaitAny
)
638 /* Check if the Object is a mutant */
639 if (CurrentObject
->Header
.Type
== MutantObject
)
641 /* Check if it's signaled */
642 if ((CurrentObject
->Header
.SignalState
> 0) ||
643 (Thread
== CurrentObject
->OwnerThread
))
645 /* This is a Wait Any, so unwait this and exit */
646 if (CurrentObject
->Header
.SignalState
!=
649 /* Normal signal state, unwait it and return */
650 KiSatisfyMutantWait(CurrentObject
, Thread
);
651 WaitStatus
= Thread
->WaitStatus
| WaitIndex
;
656 /* Raise an exception (see wasm.ru) */
657 KiReleaseDispatcherLock(Thread
->WaitIrql
);
658 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
662 else if (CurrentObject
->Header
.SignalState
> 0)
664 /* Another signaled object, unwait and return */
665 KiSatisfyNonMutantWait(CurrentObject
, Thread
);
666 WaitStatus
= WaitIndex
;
672 /* Check if we're dealing with a mutant again */
673 if (CurrentObject
->Header
.Type
== MutantObject
)
675 /* Check if it has an invalid count */
676 if ((Thread
== CurrentObject
->OwnerThread
) &&
677 (CurrentObject
->Header
.SignalState
== MINLONG
))
679 /* Raise an exception */
680 KiReleaseDispatcherLock(Thread
->WaitIrql
);
681 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
683 else if ((CurrentObject
->Header
.SignalState
<= 0) &&
684 (Thread
!= CurrentObject
->OwnerThread
))
686 /* We don't own it, can't satisfy the wait */
687 AllObjectsSignaled
= FALSE
;
690 else if (CurrentObject
->Header
.SignalState
<= 0)
692 /* Not signaled, can't satisfy */
693 AllObjectsSignaled
= FALSE
;
697 /* Set up a Wait Block for this Object */
698 WaitBlock
= &WaitBlockArray
[WaitIndex
];
699 WaitBlock
->Object
= CurrentObject
;
700 WaitBlock
->Thread
= Thread
;
701 WaitBlock
->WaitKey
= (USHORT
)WaitIndex
;
702 WaitBlock
->WaitType
= (UCHAR
)WaitType
;
703 WaitBlock
->NextWaitBlock
= &WaitBlockArray
[WaitIndex
+ 1];
706 /* Check if this is a Wait All and all the objects are signaled */
707 if ((WaitType
== WaitAll
) && (AllObjectsSignaled
))
709 /* Return to the Root Wait Block */
710 WaitBlock
->NextWaitBlock
= &WaitBlockArray
[0];
712 /* Satisfy their Waits and return to the caller */
713 KiWaitSatisfyAll(WaitBlock
);
714 WaitStatus
= Thread
->WaitStatus
;
718 /* Make sure we can satisfy the Alertable request */
719 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
720 if (WaitStatus
!= STATUS_WAIT_0
)
722 /* Unlock the dispatcher and return */
723 KiReleaseDispatcherLock(Thread
->WaitIrql
);
727 /* Enable the Timeout Timer if there was any specified */
730 /* Make sure the timeout interval isn't actually 0 */
731 if (!Timeout
->QuadPart
)
733 /* Return a timeout */
734 WaitStatus
= STATUS_TIMEOUT
;
738 /* Link timer wait block */
739 TimerWaitBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
740 WaitBlock
->NextWaitBlock
= TimerWaitBlock
;
742 /* Use this the timer block for linking below */
743 WaitBlock
= TimerWaitBlock
;
745 /* Insert the Timer into the Timer Lists and enable it */
746 ThreadTimer
= &Thread
->Timer
;
747 if (!KiInsertTimer(ThreadTimer
, *Timeout
))
749 /* Return a timeout if we couldn't insert the timer */
750 WaitStatus
= STATUS_TIMEOUT
;
754 /* Set the current due time */
755 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
758 /* Link to the Root Wait Block */
759 WaitBlock
->NextWaitBlock
= &WaitBlockArray
[0];
761 /* Insert into Object's Wait List*/
762 WaitBlock
= &WaitBlockArray
[0];
765 /* Get the Current Object */
766 CurrentObject
= WaitBlock
->Object
;
768 /* Link the Object to this Wait Block */
769 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
770 &WaitBlock
->WaitListEntry
);
772 /* Move to the next Wait Block */
773 WaitBlock
= WaitBlock
->NextWaitBlock
;
774 } while (WaitBlock
!= WaitBlockArray
);
776 /* Handle Kernel Queues */
777 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
779 /* Setup the wait information */
780 Thread
->State
= Waiting
;
782 /* Add the thread to the wait list */
783 KiAddThreadToWaitList(Thread
, Swappable
);
785 /* Swap the thread */
786 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
787 KiSetThreadSwapBusy(Thread
);
788 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
790 /* Check if we were executing an APC */
791 if (WaitStatus
== STATUS_KERNEL_APC
)
793 /* Check if we had a timeout */
796 /* Recalculate due times */
797 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
810 /* Adjust the Quantum */
811 KiAdjustQuantumThread(Thread
);
813 /* Release & Return */
814 KiReleaseDispatcherLock(Thread
->WaitIrql
);
820 NtDelayExecution(IN BOOLEAN Alertable
,
821 IN PLARGE_INTEGER DelayInterval
)
823 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
824 LARGE_INTEGER SafeInterval
;
825 NTSTATUS Status
= STATUS_SUCCESS
;
827 /* Check the previous mode */
828 if(PreviousMode
!= KernelMode
)
830 /* Enter SEH for probing */
833 /* Probe and capture the time out */
834 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
835 DelayInterval
= &SafeInterval
;
839 /* Get SEH exception */
840 Status
= _SEH_GetExceptionCode();
843 if (!NT_SUCCESS(Status
)) return Status
;
846 /* Call the Kernel Function */
847 Status
= KeDelayExecutionThread(PreviousMode
,