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