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 ******************************************************************/
16 /* PRIVATE FUNCTIONS *********************************************************/
20 KiWaitTest(IN PVOID ObjectPointer
,
21 IN KPRIORITY Increment
)
23 PLIST_ENTRY WaitEntry
, WaitList
;
24 PKWAIT_BLOCK WaitBlock
;
26 PKMUTANT FirstObject
= ObjectPointer
;
29 /* Loop the Wait Entries */
30 WaitList
= &FirstObject
->Header
.WaitListHead
;
31 WaitEntry
= WaitList
->Flink
;
32 while ((FirstObject
->Header
.SignalState
> 0) && (WaitEntry
!= WaitList
))
34 /* Get the current wait block */
35 WaitBlock
= CONTAINING_RECORD(WaitEntry
, KWAIT_BLOCK
, WaitListEntry
);
36 WaitThread
= WaitBlock
->Thread
;
37 WaitStatus
= STATUS_KERNEL_APC
;
39 /* Check the current Wait Mode */
40 if (WaitBlock
->WaitType
== WaitAny
)
42 /* Easy case, satisfy only this wait */
43 WaitStatus
= (NTSTATUS
)WaitBlock
->WaitKey
;
44 KiSatisfyObjectWait(FirstObject
, WaitThread
);
47 /* Now do the rest of the unwait */
48 KiUnwaitThread(WaitThread
, WaitStatus
, Increment
);
49 WaitEntry
= WaitList
->Flink
;
55 KiUnlinkThread(IN PKTHREAD Thread
,
56 IN NTSTATUS WaitStatus
)
58 PKWAIT_BLOCK WaitBlock
;
61 /* Update wait status */
62 Thread
->WaitStatus
|= WaitStatus
;
64 /* Remove the Wait Blocks from the list */
65 WaitBlock
= Thread
->WaitBlockList
;
69 RemoveEntryList(&WaitBlock
->WaitListEntry
);
71 /* Go to the next one */
72 WaitBlock
= WaitBlock
->NextWaitBlock
;
73 } while (WaitBlock
!= Thread
->WaitBlockList
);
75 /* Remove the thread from the wait list! */
76 if (Thread
->WaitListEntry
.Flink
) RemoveEntryList(&Thread
->WaitListEntry
);
78 /* Check if there's a Thread Timer */
79 Timer
= &Thread
->Timer
;
80 if (Timer
->Header
.Inserted
) KxRemoveTreeTimer(Timer
);
82 /* Increment the Queue's active threads */
83 if (Thread
->Queue
) Thread
->Queue
->CurrentCount
++;
86 /* Must be called with the dispatcher lock held */
89 KiUnwaitThread(IN PKTHREAD Thread
,
90 IN LONG_PTR WaitStatus
,
91 IN KPRIORITY Increment
)
93 /* Unlink the thread */
94 KiUnlinkThread(Thread
, WaitStatus
);
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
;
101 /* Reschedule the Thread */
102 KiReadyThread(Thread
);
107 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex
)
109 /* Increase contention count */
110 FastMutex
->Contention
++;
112 /* Wait for the event */
113 KeWaitForSingleObject(&FastMutex
->Gate
,
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
125 // It does NOT attempt to scan for a new thread to schedule.
129 KiExitDispatcher(IN KIRQL OldIrql
)
131 PKPRCB Prcb
= KeGetCurrentPrcb();
132 PKTHREAD Thread
, NextThread
;
135 /* Make sure we're at synchronization level */
136 ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL
);
138 /* Check if we have deferred threads */
139 KiCheckDeferredReadyList(Prcb
);
141 /* Check if we were called at dispatcher level or higher */
142 if (OldIrql
>= DISPATCH_LEVEL
)
144 /* Check if we have a thread to schedule, and that no DPC is active */
145 if ((Prcb
->NextThread
) && !(Prcb
->DpcRoutineActive
))
147 /* Request DPC interrupt */
148 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
151 /* Lower IRQL and exit */
155 /* Make sure there's a new thread scheduled */
156 if (!Prcb
->NextThread
) goto Quickie
;
159 KiAcquirePrcbLock(Prcb
);
161 /* Get the next and current threads now */
162 NextThread
= Prcb
->NextThread
;
163 Thread
= Prcb
->CurrentThread
;
165 /* Set current thread's swap busy to true */
166 KiSetThreadSwapBusy(Thread
);
168 /* Switch threads in PRCB */
169 Prcb
->NextThread
= NULL
;
170 Prcb
->CurrentThread
= NextThread
;
172 /* Set thread to running */
173 NextThread
->State
= Running
;
175 /* Queue it on the ready lists */
176 KxQueueReadyThread(Thread
, Prcb
);
179 Thread
->WaitIrql
= OldIrql
;
181 /* Swap threads and check if APCs were pending */
182 PendingApc
= KiSwapContext(Thread
, NextThread
);
185 /* Lower only to APC */
186 KeLowerIrql(APC_LEVEL
);
189 KiDeliverApc(KernelMode
, NULL
, NULL
);
190 ASSERT(OldIrql
== PASSIVE_LEVEL
);
193 /* Lower IRQl back */
195 KeLowerIrql(OldIrql
);
198 /* PUBLIC FUNCTIONS **********************************************************/
205 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode
,
206 IN BOOLEAN Alertable
,
207 IN PLARGE_INTEGER Interval OPTIONAL
)
210 PKWAIT_BLOCK TimerBlock
;
211 PKTHREAD Thread
= KeGetCurrentThread();
214 PLARGE_INTEGER OriginalDueTime
;
215 LARGE_INTEGER DueTime
, NewDueTime
, InterruptTime
;
218 /* If this is a user-mode wait of 0 seconds, yield execution */
219 if (!(Interval
->QuadPart
) && (WaitMode
!= KernelMode
))
221 /* Make sure the wait isn't alertable or interrupting an APC */
222 if (!(Alertable
) && !(Thread
->ApcState
.UserApcPending
))
224 /* Yield execution */
229 /* Setup the original time and timer/wait blocks */
230 OriginalDueTime
= Interval
;
231 Timer
= &Thread
->Timer
;
232 TimerBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
234 /* Check if the lock is already held */
235 if (!Thread
->WaitNext
) goto WaitStart
;
237 /* Otherwise, we already have the lock, so initialize the wait */
238 Thread
->WaitNext
= FALSE
;
241 /* Start wait loop */
244 /* Disable pre-emption */
245 Thread
->Preempted
= FALSE
;
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
))
251 /* Unlock the dispatcher */
252 KiReleaseDispatcherLock(Thread
->WaitIrql
);
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;
260 /* Check if the timer expired */
261 InterruptTime
.QuadPart
= KeQueryInterruptTime();
262 if ((ULONGLONG
)InterruptTime
.QuadPart
>= Timer
->DueTime
.QuadPart
)
264 /* It did, so we don't need to wait */
268 /* It didn't, so activate it */
269 Timer
->Header
.Inserted
= TRUE
;
271 /* Handle Kernel Queues */
272 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
274 /* Setup the wait information */
275 Thread
->State
= Waiting
;
277 /* Add the thread to the wait list */
278 KiAddThreadToWaitList(Thread
, Swappable
);
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());
286 /* Check if were swapped ok */
287 if (WaitStatus
!= STATUS_KERNEL_APC
)
289 /* This is a good thing */
290 if (WaitStatus
== STATUS_TIMEOUT
) WaitStatus
= STATUS_SUCCESS
;
296 /* Recalculate due times */
297 Interval
= KiRecalculateDueTime(OriginalDueTime
,
303 /* Setup a new wait */
304 Thread
->WaitIrql
= KeRaiseIrqlToSynchLevel();
306 KiAcquireDispatcherLockAtDpcLevel();
310 KiReleaseDispatcherLock(Thread
->WaitIrql
);
314 /* There was nothing to wait for. Did we have a wait interval? */
315 if (!Interval
->QuadPart
)
317 /* Unlock the dispatcher and do a yield */
318 KiReleaseDispatcherLock(Thread
->WaitIrql
);
319 return NtYieldExecution();
322 /* Unlock the dispatcher and adjust the quantum for a no-wait */
323 KiReleaseDispatcherLockFromDpcLevel();
324 KiAdjustQuantumThread(Thread
);
325 return STATUS_SUCCESS
;
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
)
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
;
346 LARGE_INTEGER DueTime
, NewDueTime
, InterruptTime
;
347 PLARGE_INTEGER OriginalDueTime
= Timeout
;
350 /* Check if the lock is already held */
351 if (!Thread
->WaitNext
) goto WaitStart
;
353 /* Otherwise, we already have the lock, so initialize the wait */
354 Thread
->WaitNext
= FALSE
;
355 KxSingleThreadWait();
357 /* Start wait loop */
360 /* Disable pre-emption */
361 Thread
->Preempted
= FALSE
;
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
))
367 /* Unlock the dispatcher */
368 KiReleaseDispatcherLock(Thread
->WaitIrql
);
373 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
375 /* Check if it's a mutant */
376 if (CurrentObject
->Header
.Type
== MutantObject
)
378 /* Check its signal state or if we own it */
379 if ((CurrentObject
->Header
.SignalState
> 0) ||
380 (Thread
== CurrentObject
->OwnerThread
))
382 /* Just unwait this guy and exit */
383 if (CurrentObject
->Header
.SignalState
!= (LONG
)MINLONG
)
385 /* It has a normal signal state. Unwait and return */
386 KiSatisfyMutantWait(CurrentObject
, Thread
);
387 WaitStatus
= Thread
->WaitStatus
;
392 /* Raise an exception */
393 KiReleaseDispatcherLock(Thread
->WaitIrql
);
394 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
398 else if (CurrentObject
->Header
.SignalState
> 0)
400 /* Another satisfied object */
401 KiSatisfyNonMutantWait(CurrentObject
);
402 WaitStatus
= STATUS_WAIT_0
;
406 /* Make sure we can satisfy the Alertable request */
407 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
408 if (WaitStatus
!= STATUS_WAIT_0
) break;
410 /* Enable the Timeout Timer if there was any specified */
413 /* Check if the timer expired */
414 InterruptTime
.QuadPart
= KeQueryInterruptTime();
415 if ((ULONGLONG
)InterruptTime
.QuadPart
>=
416 Timer
->DueTime
.QuadPart
)
418 /* It did, so we don't need to wait */
419 WaitStatus
= STATUS_TIMEOUT
;
423 /* It didn't, so activate it */
424 Timer
->Header
.Inserted
= TRUE
;
427 /* Link the Object to this Wait Block */
428 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
429 &WaitBlock
->WaitListEntry
);
431 /* Handle Kernel Queues */
432 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
434 /* Setup the wait information */
435 Thread
->State
= Waiting
;
437 /* Add the thread to the wait list */
438 KiAddThreadToWaitList(Thread
, Swappable
);
440 /* Activate thread swap */
441 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
442 KiSetThreadSwapBusy(Thread
);
444 /* Check if we have a timer */
448 KxInsertTimer(Timer
, Hand
);
452 /* Otherwise, unlock the dispatcher */
453 KiReleaseDispatcherLockFromDpcLevel();
456 /* Do the actual swap */
457 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
459 /* Check if we were executing an APC */
460 if (WaitStatus
!= STATUS_KERNEL_APC
) return WaitStatus
;
462 /* Check if we had a timeout */
465 /* Recalculate due times */
466 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
472 /* Setup a new wait */
473 Thread
->WaitIrql
= KeRaiseIrqlToSynchLevel();
474 KxSingleThreadWait();
475 KiAcquireDispatcherLockAtDpcLevel();
479 KiReleaseDispatcherLock(Thread
->WaitIrql
);
483 /* Release dispatcher lock but maintain high IRQL */
484 KiReleaseDispatcherLockFromDpcLevel();
486 /* Adjust the Quantum and return the wait status */
487 KiAdjustQuantumThread(Thread
);
496 KeWaitForMultipleObjects(IN ULONG Count
,
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
)
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
;
512 PLARGE_INTEGER OriginalDueTime
= Timeout
;
513 LARGE_INTEGER DueTime
, NewDueTime
, InterruptTime
;
514 ULONG Index
, Hand
= 0;
516 /* Make sure the Wait Count is valid */
519 /* Check in regards to the Thread Object Limit */
520 if (Count
> THREAD_WAIT_OBJECTS
)
523 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
526 /* Use the Thread's Wait Block */
527 WaitBlockArray
= &Thread
->WaitBlock
[0];
531 /* Using our own Block Array, so check with the System Object Limit */
532 if (Count
> MAXIMUM_WAIT_OBJECTS
)
535 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
542 /* Check if the lock is already held */
543 if (!Thread
->WaitNext
) goto WaitStart
;
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 */
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. */
555 /* Start wait loop */
558 /* Disable pre-emption */
559 Thread
->Preempted
= FALSE
;
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
))
565 /* Unlock the dispatcher */
566 KiReleaseDispatcherLock(Thread
->WaitIrql
);
570 /* Check what kind of wait this is */
572 if (WaitType
== WaitAny
)
577 /* Get the Current Object */
578 CurrentObject
= (PKMUTANT
)Object
[Index
];
579 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
581 /* Check if the Object is a mutant */
582 if (CurrentObject
->Header
.Type
== MutantObject
)
584 /* Check if it's signaled */
585 if ((CurrentObject
->Header
.SignalState
> 0) ||
586 (Thread
== CurrentObject
->OwnerThread
))
588 /* This is a Wait Any, so unwait this and exit */
589 if (CurrentObject
->Header
.SignalState
!=
592 /* Normal signal state, unwait it and return */
593 KiSatisfyMutantWait(CurrentObject
, Thread
);
594 WaitStatus
= Thread
->WaitStatus
| Index
;
599 /* Raise an exception (see wasm.ru) */
600 KiReleaseDispatcherLock(Thread
->WaitIrql
);
601 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
605 else if (CurrentObject
->Header
.SignalState
> 0)
607 /* Another signaled object, unwait and return */
608 KiSatisfyNonMutantWait(CurrentObject
);
613 /* Go to the next block */
615 } while (Index
< Count
);
622 /* Get the Current Object */
623 CurrentObject
= (PKMUTANT
)Object
[Index
];
624 ASSERT(CurrentObject
->Header
.Type
!= QueueObject
);
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
== (LONG
)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 */
644 else if (CurrentObject
->Header
.SignalState
<= 0)
646 /* Not signaled, can't satisfy */
650 /* Go to the next block */
652 } while (Index
< Count
);
654 /* Check if we've went through all the objects */
657 /* Loop wait blocks */
658 WaitBlock
= WaitBlockArray
;
661 /* Get the object and satisfy it */
662 CurrentObject
= (PKMUTANT
)WaitBlock
->Object
;
663 KiSatisfyObjectWait(CurrentObject
, Thread
);
665 /* Go to the next block */
666 WaitBlock
= WaitBlock
->NextWaitBlock
;
667 } while(WaitBlock
!= WaitBlockArray
);
669 /* Set the wait status and get out */
670 WaitStatus
= Thread
->WaitStatus
;
675 /* Make sure we can satisfy the Alertable request */
676 WaitStatus
= KiCheckAlertability(Thread
, Alertable
, WaitMode
);
677 if (WaitStatus
!= STATUS_WAIT_0
) break;
679 /* Enable the Timeout Timer if there was any specified */
682 /* Check if the timer expired */
683 InterruptTime
.QuadPart
= KeQueryInterruptTime();
684 if ((ULONGLONG
)InterruptTime
.QuadPart
>=
685 Timer
->DueTime
.QuadPart
)
687 /* It did, so we don't need to wait */
688 WaitStatus
= STATUS_TIMEOUT
;
692 /* It didn't, so activate it */
693 Timer
->Header
.Inserted
= TRUE
;
695 /* Link the wait blocks */
696 WaitBlock
->NextWaitBlock
= TimerBlock
;
699 /* Insert into Object's Wait List*/
700 WaitBlock
= WaitBlockArray
;
703 /* Get the Current Object */
704 CurrentObject
= WaitBlock
->Object
;
706 /* Link the Object to this Wait Block */
707 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
708 &WaitBlock
->WaitListEntry
);
710 /* Move to the next Wait Block */
711 WaitBlock
= WaitBlock
->NextWaitBlock
;
712 } while (WaitBlock
!= WaitBlockArray
);
714 /* Handle Kernel Queues */
715 if (Thread
->Queue
) KiActivateWaiterQueue(Thread
->Queue
);
717 /* Setup the wait information */
718 Thread
->State
= Waiting
;
720 /* Add the thread to the wait list */
721 KiAddThreadToWaitList(Thread
, Swappable
);
723 /* Activate thread swap */
724 ASSERT(Thread
->WaitIrql
<= DISPATCH_LEVEL
);
725 KiSetThreadSwapBusy(Thread
);
727 /* Check if we have a timer */
731 KxInsertTimer(Timer
, Hand
);
735 /* Otherwise, unlock the dispatcher */
736 KiReleaseDispatcherLockFromDpcLevel();
739 /* Swap the thread */
740 WaitStatus
= KiSwapThread(Thread
, KeGetCurrentPrcb());
742 /* Check if we were executing an APC */
743 if (WaitStatus
!= STATUS_KERNEL_APC
) return WaitStatus
;
745 /* Check if we had a timeout */
748 /* Recalculate due times */
749 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
756 /* Setup a new wait */
757 Thread
->WaitIrql
= KeRaiseIrqlToSynchLevel();
759 KiAcquireDispatcherLockAtDpcLevel();
763 KiReleaseDispatcherLock(Thread
->WaitIrql
);
767 /* Release dispatcher lock but maintain high IRQL */
768 KiReleaseDispatcherLockFromDpcLevel();
770 /* Adjust the Quantum and return the wait status */
771 KiAdjustQuantumThread(Thread
);
777 NtDelayExecution(IN BOOLEAN Alertable
,
778 IN PLARGE_INTEGER DelayInterval
)
780 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
781 LARGE_INTEGER SafeInterval
;
782 NTSTATUS Status
= STATUS_SUCCESS
;
784 /* Check the previous mode */
785 if(PreviousMode
!= KernelMode
)
787 /* Enter SEH for probing */
790 /* Probe and capture the time out */
791 SafeInterval
= ProbeForReadLargeInteger(DelayInterval
);
792 DelayInterval
= &SafeInterval
;
794 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
796 /* Get SEH exception */
797 Status
= _SEH2_GetExceptionCode();
800 if (!NT_SUCCESS(Status
)) return Status
;
803 /* Call the Kernel Function */
804 Status
= KeDelayExecutionThread(PreviousMode
,