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