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