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