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)
10 /* INCLUDES ******************************************************************/
14 #include <internal/debug.h>
16 /* PRIVATE FUNCTIONS *********************************************************/
20 KiWaitSatisfyAll(PKWAIT_BLOCK FirstBlock
)
22 PKWAIT_BLOCK WaitBlock
= FirstBlock
;
23 PKTHREAD WaitThread
= WaitBlock
->Thread
;
25 /* Loop through all the Wait Blocks, and wake each Object */
28 /* Make sure it hasn't timed out */
29 if (WaitBlock
->WaitKey
!= STATUS_TIMEOUT
)
32 KiSatisfyObjectWait((PKMUTANT
)WaitBlock
->Object
, WaitThread
);
35 /* Move to the next block */
36 WaitBlock
= WaitBlock
->NextWaitBlock
;
37 } while (WaitBlock
!= FirstBlock
);
42 KiWaitTest(IN PVOID ObjectPointer
,
43 IN KPRIORITY Increment
)
45 PLIST_ENTRY WaitEntry
, WaitList
;
46 PKWAIT_BLOCK WaitBlock
;
48 PKMUTANT FirstObject
= ObjectPointer
;
51 /* Loop the Wait Entries */
52 WaitList
= &FirstObject
->Header
.WaitListHead
;
53 WaitEntry
= WaitList
->Flink
;
54 while ((FirstObject
->Header
.SignalState
> 0) && (WaitEntry
!= WaitList
))
56 /* Get the current wait block */
57 WaitBlock
= CONTAINING_RECORD(WaitEntry
, KWAIT_BLOCK
, WaitListEntry
);
58 WaitThread
= WaitBlock
->Thread
;
59 WaitStatus
= STATUS_KERNEL_APC
;
61 /* Check the current Wait Mode */
62 if (WaitBlock
->WaitType
== WaitAny
)
64 /* Easy case, satisfy only this wait */
65 WaitStatus
= (NTSTATUS
)WaitBlock
->WaitKey
;
66 KiSatisfyObjectWait(FirstObject
, WaitThread
);
69 /* Now do the rest of the unwait */
70 KiUnwaitThread(WaitThread
, WaitStatus
, Increment
);
71 WaitEntry
= WaitList
->Flink
;
77 KiUnlinkThread(IN PKTHREAD Thread
,
78 IN NTSTATUS WaitStatus
)
80 PKWAIT_BLOCK WaitBlock
;
83 /* Update wait status */
84 Thread
->WaitStatus
|= WaitStatus
;
86 /* Remove the Wait Blocks from the list */
87 WaitBlock
= Thread
->WaitBlockList
;
91 RemoveEntryList(&WaitBlock
->WaitListEntry
);
93 /* Go to the next one */
94 WaitBlock
= WaitBlock
->NextWaitBlock
;
95 } while (WaitBlock
!= Thread
->WaitBlockList
);
97 /* Remove the thread from the wait list! */
98 if (Thread
->WaitListEntry
.Flink
) RemoveEntryList(&Thread
->WaitListEntry
);
100 /* Check if there's a Thread Timer */
101 Timer
= &Thread
->Timer
;
102 if (Timer
->Header
.Inserted
)
104 /* Remove the timer */
105 Timer
->Header
.Inserted
= FALSE
;
106 RemoveEntryList(&Timer
->TimerListEntry
);
107 //KiRemoveTimer(Timer);
110 /* Increment the Queue's active threads */
111 if (Thread
->Queue
) Thread
->Queue
->CurrentCount
++;
114 /* Must be called with the dispatcher lock held */
117 KiUnwaitThread(IN PKTHREAD Thread
,
118 IN NTSTATUS WaitStatus
,
119 IN KPRIORITY Increment
)
121 /* Unlink the thread */
122 KiUnlinkThread(Thread
, WaitStatus
);
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
;
129 /* Reschedule the Thread */
130 KiReadyThread(Thread
);
135 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex
)
137 /* Increase contention count */
138 FastMutex
->Contention
++;
140 /* Wait for the event */
141 KeWaitForSingleObject(&FastMutex
->Gate
,
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
153 // It does NOT attempt to scan for a new thread to schedule.
157 KiExitDispatcher(IN KIRQL OldIrql
)
159 PKPRCB Prcb
= KeGetCurrentPrcb();
160 PKTHREAD Thread
, NextThread
;
163 /* Make sure we're at synchronization level */
164 ASSERT_IRQL(SYNCH_LEVEL
);
166 /* Check if we have deferred threads */
167 KiCheckDeferredReadyList(Prcb
);
169 /* Check if we were called at dispatcher level or higher */
170 if (OldIrql
>= DISPATCH_LEVEL
)
172 /* Check if we have a thread to schedule, and that no DPC is active */
173 if ((Prcb
->NextThread
) && !(Prcb
->DpcRoutineActive
))
175 /* Request DPC interrupt */
176 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
179 /* Lower IRQL and exit */
183 /* Make sure there's a new thread scheduled */
184 if (!Prcb
->NextThread
) goto Quickie
;
187 KiAcquirePrcbLock(Prcb
);
189 /* Get the next and current threads now */
190 NextThread
= Prcb
->NextThread
;
191 Thread
= Prcb
->CurrentThread
;
193 /* Set current thread's swap busy to true */
194 KiSetThreadSwapBusy(Thread
);
196 /* Switch threads in PRCB */
197 Prcb
->NextThread
= NULL
;
198 Prcb
->CurrentThread
= NextThread
;
200 /* Set thread to running */
201 NextThread
->State
= Running
;
203 /* Queue it on the ready lists */
204 KxQueueReadyThread(Thread
, Prcb
);
207 Thread
->WaitIrql
= OldIrql
;
209 /* Swap threads and check if APCs were pending */
210 PendingApc
= KiSwapContext(Thread
, NextThread
);
213 /* Lower only to APC */
214 KeLowerIrql(APC_LEVEL
);
217 KiDeliverApc(KernelMode
, NULL
, NULL
);
218 ASSERT(OldIrql
== PASSIVE_LEVEL
);
221 /* Lower IRQl back */
223 KeLowerIrql(OldIrql
);
226 /* PUBLIC FUNCTIONS **********************************************************/
233 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode
,
234 IN BOOLEAN Alertable
,
235 IN PLARGE_INTEGER Interval OPTIONAL
)
238 PKTHREAD Thread
= KeGetCurrentThread();
241 PLARGE_INTEGER OriginalDueTime
= Interval
;
242 LARGE_INTEGER DueTime
, NewDueTime
;
244 /* Check if the lock is already held */
245 if (Thread
->WaitNext
)
247 /* Lock is held, disable Wait Next */
248 Thread
->WaitNext
= FALSE
;
249 Swappable
= KxDelayThreadWait(Thread
, Alertable
, WaitMode
);
253 /* Lock not held, acquire it */
255 Thread
->WaitIrql
= KiAcquireDispatcherLock();
256 Swappable
= KxDelayThreadWait(Thread
, Alertable
, WaitMode
);
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
))
263 /* Unlock the dispatcher */
264 KiReleaseDispatcherLock(Thread
->WaitIrql
);
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
)
272 /* Unlock the dispatcher and return */
273 KiReleaseDispatcherLock(Thread
->WaitIrql
);
278 ThreadTimer
= &Thread
->Timer
;
280 /* Insert the Timer into the Timer Lists and enable it */
281 if (!KiInsertTimer(ThreadTimer
, *Interval
))
283 /* FIXME: We should find a new ready thread */
284 KiReleaseDispatcherLock(Thread
->WaitIrql
);
285 return STATUS_WAIT_0
;
289 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
291 /* Handle Kernel Queues */
292 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
294 /* Setup the wait information */
295 Thread
->State
= Waiting
;
297 /* Add the thread to the wait list */
298 KiAddThreadToWaitList(Thread
, Swappable
);
300 /* Swap the thread */
301 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
302 KiSetThreadSwapBusy(Thread
);
303 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
305 /* Check if we were executing an APC or if we timed out */
306 if (WaitStatus
== STATUS_KERNEL_APC
)
308 /* Recalculate due times */
309 Interval
= KiRecalculateDueTime(OriginalDueTime
,
315 /* This is a good thing */
316 if (WaitStatus
== STATUS_TIMEOUT
) WaitStatus
= STATUS_SUCCESS
;
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
)
333 PKMUTANT CurrentObject
;
334 PKWAIT_BLOCK WaitBlock
;
336 PKTHREAD Thread
= KeGetCurrentThread();
339 LARGE_INTEGER DueTime
, NewDueTime
;
340 PLARGE_INTEGER OriginalDueTime
= Timeout
;
343 WaitBlock
= &Thread
->WaitBlock
[0];
345 /* Check if the lock is already held */
346 if (Thread
->WaitNext
)
348 /* Lock is held, disable Wait Next */
349 Thread
->WaitNext
= FALSE
;
350 Swappable
= KxSingleThreadWait(Thread
,
361 /* Lock not held, acquire it */
362 Thread
->WaitIrql
= KiAcquireDispatcherLock();
363 Swappable
= KxSingleThreadWait(Thread
,
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
))
376 /* Unlock the dispatcher and wait again */
377 KiReleaseDispatcherLock(Thread
->WaitIrql
);
381 /* Get the Current Object */
382 CurrentObject
= (PKMUTANT
)Object
;
383 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
385 /* Check if it's a mutant */
386 if (CurrentObject
->Header
.Type
== MutantObject
)
388 /* Check its signal state or if we own it */
389 if ((CurrentObject
->Header
.SignalState
> 0) ||
390 (Thread
== CurrentObject
->OwnerThread
))
392 /* Just unwait this guy and exit */
393 if (CurrentObject
->Header
.SignalState
!= (LONG
)MINLONG
)
395 /* It has a normal signal state. Unwait and return */
396 KiSatisfyMutantWait(CurrentObject
, Thread
);
397 WaitStatus
= Thread
->WaitStatus
;
402 /* Raise an exception */
403 KiReleaseDispatcherLock(Thread
->WaitIrql
);
404 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
408 else if (CurrentObject
->Header
.SignalState
> 0)
410 /* Another satisfied object */
411 KiSatisfyNonMutantWait(CurrentObject
, Thread
);
412 WaitStatus
= STATUS_WAIT_0
;
416 /* Make sure we can satisfy the Alertable request */
417 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
418 if (WaitStatus
!= STATUS_WAIT_0
)
420 /* Unlock the dispatcher and return */
421 KiReleaseDispatcherLock(Thread
->WaitIrql
);
425 /* Enable the Timeout Timer if there was any specified */
428 /* Fail if the timeout interval is actually 0 */
429 if (!Timeout
->QuadPart
)
431 /* Return a timeout */
432 WaitStatus
= STATUS_TIMEOUT
;
436 /* Insert the Timer into the Timer Lists and enable it */
437 ThreadTimer
= &Thread
->Timer
;
438 if (!KiInsertTimer(ThreadTimer
, *Timeout
))
440 /* Return a timeout if we couldn't insert the timer */
441 WaitStatus
= STATUS_TIMEOUT
;
445 /* Set the current due time */
446 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
449 /* Link the Object to this Wait Block */
450 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
451 &WaitBlock
->WaitListEntry
);
453 /* Handle Kernel Queues */
454 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
456 /* Setup the wait information */
457 Thread
->State
= Waiting
;
459 /* Add the thread to the wait list */
460 KiAddThreadToWaitList(Thread
, Swappable
);
462 /* Swap the thread */
463 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
464 KiSetThreadSwapBusy(Thread
);
465 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
467 /* Check if we were executing an APC */
468 if (WaitStatus
== STATUS_KERNEL_APC
)
470 /* Check if we had a timeout */
473 /* Recalculate due times */
474 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
487 /* Adjust the Quantum */
488 KiAdjustQuantumThread(Thread
);
490 /* Release & Return */
491 KiReleaseDispatcherLock(Thread
->WaitIrql
);
500 KeWaitForMultipleObjects(IN ULONG Count
,
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
)
509 PKMUTANT CurrentObject
;
510 PKWAIT_BLOCK WaitBlock
;
511 PKWAIT_BLOCK TimerWaitBlock
;
513 PKTHREAD Thread
= KeGetCurrentThread();
514 ULONG AllObjectsSignaled
;
516 NTSTATUS WaitStatus
= STATUS_SUCCESS
;
518 PLARGE_INTEGER OriginalDueTime
= Timeout
;
519 LARGE_INTEGER DueTime
, NewDueTime
;
521 /* Make sure the Wait Count is valid */
524 /* Check in regards to the Thread Object Limit */
525 if (Count
> THREAD_WAIT_OBJECTS
)
528 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
531 /* Use the Thread's Wait Block */
532 WaitBlockArray
= &Thread
->WaitBlock
[0];
536 /* Using our own Block Array, so check with the System Object Limit */
537 if (Count
> MAXIMUM_WAIT_OBJECTS
)
540 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
547 /* Check if the lock is already held */
548 if (Thread
->WaitNext
)
550 /* Lock is held, disable Wait Next */
551 Thread
->WaitNext
= FALSE
;
555 /* Lock not held, acquire it */
557 Thread
->WaitIrql
= KiAcquireDispatcherLock();
560 /* Prepare for the wait */
561 Swappable
= KxMultiThreadWait(Thread
,
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
))
571 /* Unlock the dispatcher */
572 KiReleaseDispatcherLock(Thread
->WaitIrql
);
576 /* Append wait block to the KTHREAD wait block list */
577 WaitBlock
= WaitBlockArray
;
579 /* Check if the wait is (already) satisfied */
580 AllObjectsSignaled
= TRUE
;
582 /* First, we'll try to satisfy the wait directly */
583 for (WaitIndex
= 0; WaitIndex
< Count
; WaitIndex
++)
585 /* Get the Current Object */
586 CurrentObject
= (PKMUTANT
)Object
[WaitIndex
];
587 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
589 /* Check the type of wait */
590 if (WaitType
== WaitAny
)
592 /* Check if the Object is a mutant */
593 if (CurrentObject
->Header
.Type
== MutantObject
)
595 /* Check if it's signaled */
596 if ((CurrentObject
->Header
.SignalState
> 0) ||
597 (Thread
== CurrentObject
->OwnerThread
))
599 /* This is a Wait Any, so unwait this and exit */
600 if (CurrentObject
->Header
.SignalState
!=
603 /* Normal signal state, unwait it and return */
604 KiSatisfyMutantWait(CurrentObject
, Thread
);
605 WaitStatus
= Thread
->WaitStatus
| WaitIndex
;
610 /* Raise an exception (see wasm.ru) */
611 KiReleaseDispatcherLock(Thread
->WaitIrql
);
612 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
616 else if (CurrentObject
->Header
.SignalState
> 0)
618 /* Another signaled object, unwait and return */
619 KiSatisfyNonMutantWait(CurrentObject
, Thread
);
620 WaitStatus
= WaitIndex
;
626 /* Check if we're dealing with a mutant again */
627 if (CurrentObject
->Header
.Type
== MutantObject
)
629 /* Check if it has an invalid count */
630 if ((Thread
== CurrentObject
->OwnerThread
) &&
631 (CurrentObject
->Header
.SignalState
== MINLONG
))
633 /* Raise an exception */
634 KiReleaseDispatcherLock(Thread
->WaitIrql
);
635 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
637 else if ((CurrentObject
->Header
.SignalState
<= 0) &&
638 (Thread
!= CurrentObject
->OwnerThread
))
640 /* We don't own it, can't satisfy the wait */
641 AllObjectsSignaled
= FALSE
;
644 else if (CurrentObject
->Header
.SignalState
<= 0)
646 /* Not signaled, can't satisfy */
647 AllObjectsSignaled
= FALSE
;
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];
660 /* Check if this is a Wait All and all the objects are signaled */
661 if ((WaitType
== WaitAll
) && (AllObjectsSignaled
))
663 /* Return to the Root Wait Block */
664 WaitBlock
->NextWaitBlock
= &WaitBlockArray
[0];
666 /* Satisfy their Waits and return to the caller */
667 KiWaitSatisfyAll(WaitBlock
);
668 WaitStatus
= Thread
->WaitStatus
;
672 /* Make sure we can satisfy the Alertable request */
673 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
674 if (WaitStatus
!= STATUS_WAIT_0
)
676 /* Unlock the dispatcher and return */
677 KiReleaseDispatcherLock(Thread
->WaitIrql
);
681 /* Enable the Timeout Timer if there was any specified */
684 /* Make sure the timeout interval isn't actually 0 */
685 if (!Timeout
->QuadPart
)
687 /* Return a timeout */
688 WaitStatus
= STATUS_TIMEOUT
;
692 /* Link timer wait block */
693 TimerWaitBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
694 WaitBlock
->NextWaitBlock
= TimerWaitBlock
;
696 /* Use this the timer block for linking below */
697 WaitBlock
= TimerWaitBlock
;
699 /* Insert the Timer into the Timer Lists and enable it */
700 ThreadTimer
= &Thread
->Timer
;
701 if (!KiInsertTimer(ThreadTimer
, *Timeout
))
703 /* Return a timeout if we couldn't insert the timer */
704 WaitStatus
= STATUS_TIMEOUT
;
708 /* Set the current due time */
709 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
712 /* Link to the Root Wait Block */
713 WaitBlock
->NextWaitBlock
= &WaitBlockArray
[0];
715 /* Insert into Object's Wait List*/
716 WaitBlock
= &WaitBlockArray
[0];
719 /* Get the Current Object */
720 CurrentObject
= WaitBlock
->Object
;
722 /* Link the Object to this Wait Block */
723 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
724 &WaitBlock
->WaitListEntry
);
726 /* Move to the next Wait Block */
727 WaitBlock
= WaitBlock
->NextWaitBlock
;
728 } while (WaitBlock
!= WaitBlockArray
);
730 /* Handle Kernel Queues */
731 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
733 /* Setup the wait information */
734 Thread
->State
= Waiting
;
736 /* Add the thread to the wait list */
737 KiAddThreadToWaitList(Thread
, Swappable
);
739 /* Swap the thread */
740 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
741 KiSetThreadSwapBusy(Thread
);
742 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
744 /* Check if we were executing an APC */
745 if (WaitStatus
== STATUS_KERNEL_APC
)
747 /* Check if we had a timeout */
750 /* Recalculate due times */
751 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
764 /* Adjust the Quantum */
765 KiAdjustQuantumThread(Thread
);
767 /* Release & Return */
768 KiReleaseDispatcherLock(Thread
->WaitIrql
);
774 NtDelayExecution(IN BOOLEAN Alertable
,
775 IN PLARGE_INTEGER DelayInterval
)
777 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
778 LARGE_INTEGER SafeInterval
;
779 NTSTATUS Status
= STATUS_SUCCESS
;
781 /* Check the previous mode */
782 if(PreviousMode
!= KernelMode
)
784 /* Enter SEH for probing */
787 /* Probe and capture the time out */
788 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
789 DelayInterval
= &SafeInterval
;
793 /* Get SEH exception */
794 Status
= _SEH_GetExceptionCode();
797 if (!NT_SUCCESS(Status
)) return Status
;
800 /* Call the Kernel Function */
801 Status
= KeDelayExecutionThread(PreviousMode
,