f36c0fff4b7a7f3b6c5040aba225464cccf5f19e
[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 LONG_PTR 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 /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */
548 /* uses (and modifies some of) the following local */
549 /* variables: */
550 /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */
551 /* If it looks like this code doesn't actually wait for any objects */
552 /* at all, it's because the setup is done by that macro. */
553 KxMultiThreadWait();
554
555 /* Start wait loop */
556 for (;;)
557 {
558 /* Disable pre-emption */
559 Thread->Preempted = FALSE;
560
561 /* Check if a kernel APC is pending and we're below APC_LEVEL */
562 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
563 (Thread->WaitIrql < APC_LEVEL))
564 {
565 /* Unlock the dispatcher */
566 KiReleaseDispatcherLock(Thread->WaitIrql);
567 }
568 else
569 {
570 /* Check what kind of wait this is */
571 Index = 0;
572 if (WaitType == WaitAny)
573 {
574 /* Loop blocks */
575 do
576 {
577 /* Get the Current Object */
578 CurrentObject = (PKMUTANT)Object[Index];
579 ASSERT(CurrentObject->Header.Type != QueueObject);
580
581 /* Check if the Object is a mutant */
582 if (CurrentObject->Header.Type == MutantObject)
583 {
584 /* Check if it's signaled */
585 if ((CurrentObject->Header.SignalState > 0) ||
586 (Thread == CurrentObject->OwnerThread))
587 {
588 /* This is a Wait Any, so unwait this and exit */
589 if (CurrentObject->Header.SignalState !=
590 (LONG)MINLONG)
591 {
592 /* Normal signal state, unwait it and return */
593 KiSatisfyMutantWait(CurrentObject, Thread);
594 WaitStatus = Thread->WaitStatus | Index;
595 goto DontWait;
596 }
597 else
598 {
599 /* Raise an exception (see wasm.ru) */
600 KiReleaseDispatcherLock(Thread->WaitIrql);
601 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
602 }
603 }
604 }
605 else if (CurrentObject->Header.SignalState > 0)
606 {
607 /* Another signaled object, unwait and return */
608 KiSatisfyNonMutantWait(CurrentObject);
609 WaitStatus = Index;
610 goto DontWait;
611 }
612
613 /* Go to the next block */
614 Index++;
615 } while (Index < Count);
616 }
617 else
618 {
619 /* Loop blocks */
620 do
621 {
622 /* Get the Current Object */
623 CurrentObject = (PKMUTANT)Object[Index];
624 ASSERT(CurrentObject->Header.Type != QueueObject);
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 == (LONG)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 break;
642 }
643 }
644 else if (CurrentObject->Header.SignalState <= 0)
645 {
646 /* Not signaled, can't satisfy */
647 break;
648 }
649
650 /* Go to the next block */
651 Index++;
652 } while (Index < Count);
653
654 /* Check if we've went through all the objects */
655 if (Index == Count)
656 {
657 /* Loop wait blocks */
658 WaitBlock = WaitBlockArray;
659 do
660 {
661 /* Get the object and satisfy it */
662 CurrentObject = (PKMUTANT)WaitBlock->Object;
663 KiSatisfyObjectWait(CurrentObject, Thread);
664
665 /* Go to the next block */
666 WaitBlock = WaitBlock->NextWaitBlock;
667 } while(WaitBlock != WaitBlockArray);
668
669 /* Set the wait status and get out */
670 WaitStatus = Thread->WaitStatus;
671 goto DontWait;
672 }
673 }
674
675 /* Make sure we can satisfy the Alertable request */
676 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
677 if (WaitStatus != STATUS_WAIT_0) break;
678
679 /* Enable the Timeout Timer if there was any specified */
680 if (Timeout)
681 {
682 /* Check if the timer expired */
683 InterruptTime.QuadPart = KeQueryInterruptTime();
684 if ((ULONGLONG)InterruptTime.QuadPart >=
685 Timer->DueTime.QuadPart)
686 {
687 /* It did, so we don't need to wait */
688 WaitStatus = STATUS_TIMEOUT;
689 goto DontWait;
690 }
691
692 /* It didn't, so activate it */
693 Timer->Header.Inserted = TRUE;
694
695 /* Link the wait blocks */
696 WaitBlock->NextWaitBlock = TimerBlock;
697 }
698
699 /* Insert into Object's Wait List*/
700 WaitBlock = WaitBlockArray;
701 do
702 {
703 /* Get the Current Object */
704 CurrentObject = WaitBlock->Object;
705
706 /* Link the Object to this Wait Block */
707 InsertTailList(&CurrentObject->Header.WaitListHead,
708 &WaitBlock->WaitListEntry);
709
710 /* Move to the next Wait Block */
711 WaitBlock = WaitBlock->NextWaitBlock;
712 } while (WaitBlock != WaitBlockArray);
713
714 /* Handle Kernel Queues */
715 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
716
717 /* Setup the wait information */
718 Thread->State = Waiting;
719
720 /* Add the thread to the wait list */
721 KiAddThreadToWaitList(Thread, Swappable);
722
723 /* Activate thread swap */
724 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
725 KiSetThreadSwapBusy(Thread);
726
727 /* Check if we have a timer */
728 if (Timeout)
729 {
730 /* Insert it */
731 KxInsertTimer(Timer, Hand);
732 }
733 else
734 {
735 /* Otherwise, unlock the dispatcher */
736 KiReleaseDispatcherLockFromDpcLevel();
737 }
738
739 /* Swap the thread */
740 WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
741
742 /* Check if we were executing an APC */
743 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
744
745 /* Check if we had a timeout */
746 if (Timeout)
747 {
748 /* Recalculate due times */
749 Timeout = KiRecalculateDueTime(OriginalDueTime,
750 &DueTime,
751 &NewDueTime);
752 }
753 }
754
755 WaitStart:
756 /* Setup a new wait */
757 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
758 KxMultiThreadWait();
759 KiAcquireDispatcherLockAtDpcLevel();
760 }
761
762 /* We are done */
763 KiReleaseDispatcherLock(Thread->WaitIrql);
764 return WaitStatus;
765
766 DontWait:
767 /* Release dispatcher lock but maintain high IRQL */
768 KiReleaseDispatcherLockFromDpcLevel();
769
770 /* Adjust the Quantum and return the wait status */
771 KiAdjustQuantumThread(Thread);
772 return WaitStatus;
773 }
774
775 NTSTATUS
776 NTAPI
777 NtDelayExecution(IN BOOLEAN Alertable,
778 IN PLARGE_INTEGER DelayInterval)
779 {
780 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
781 LARGE_INTEGER SafeInterval;
782 NTSTATUS Status = STATUS_SUCCESS;
783
784 /* Check the previous mode */
785 if(PreviousMode != KernelMode)
786 {
787 /* Enter SEH for probing */
788 _SEH2_TRY
789 {
790 /* Probe and capture the time out */
791 SafeInterval = ProbeForReadLargeInteger(DelayInterval);
792 DelayInterval = &SafeInterval;
793 }
794 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
795 {
796 /* Get SEH exception */
797 Status = _SEH2_GetExceptionCode();
798 }
799 _SEH2_END;
800 if (!NT_SUCCESS(Status)) return Status;
801 }
802
803 /* Call the Kernel Function */
804 Status = KeDelayExecutionThread(PreviousMode,
805 Alertable,
806 DelayInterval);
807
808 /* Return Status */
809 return Status;
810 }
811
812 /* EOF */