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