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 /* GLOBALS ******************************************************************/
18 KSPIN_LOCK DispatcherDatabaseLock
;
20 /* PRIVATE FUNCTIONS *********************************************************/
24 KiWaitSatisfyAll(PKWAIT_BLOCK FirstBlock
)
26 PKWAIT_BLOCK WaitBlock
= FirstBlock
;
27 PKTHREAD WaitThread
= WaitBlock
->Thread
;
29 /* Loop through all the Wait Blocks, and wake each Object */
32 /* Make sure it hasn't timed out */
33 if (WaitBlock
->WaitKey
!= STATUS_TIMEOUT
)
36 KiSatisfyObjectWait((PKMUTANT
)WaitBlock
->Object
, WaitThread
);
39 /* Move to the next block */
40 WaitBlock
= WaitBlock
->NextWaitBlock
;
41 } while (WaitBlock
!= FirstBlock
);
46 KiWaitTest(IN PVOID ObjectPointer
,
47 IN KPRIORITY Increment
)
49 PLIST_ENTRY WaitEntry
;
51 PKWAIT_BLOCK CurrentWaitBlock
;
52 PKWAIT_BLOCK NextWaitBlock
;
54 PKMUTANT FirstObject
= ObjectPointer
, Object
;
56 /* Loop the Wait Entries */
57 WaitList
= &FirstObject
->Header
.WaitListHead
;
58 WaitEntry
= WaitList
->Flink
;
59 while ((FirstObject
->Header
.SignalState
> 0) &&
60 (WaitEntry
!= WaitList
))
62 /* Get the current wait block */
63 CurrentWaitBlock
= CONTAINING_RECORD(WaitEntry
,
66 WaitThread
= CurrentWaitBlock
->Thread
;
68 /* Check the current Wait Mode */
69 if (CurrentWaitBlock
->WaitType
== WaitAny
)
71 /* Easy case, satisfy only this wait */
72 WaitEntry
= WaitEntry
->Blink
;
73 KiSatisfyObjectWait(FirstObject
, WaitThread
);
77 /* Everything must be satisfied */
78 NextWaitBlock
= CurrentWaitBlock
->NextWaitBlock
;
80 /* Loop first to make sure they are valid */
81 while (NextWaitBlock
!= CurrentWaitBlock
)
83 /* Make sure this isn't a timeout block */
84 if (NextWaitBlock
->WaitKey
!= STATUS_TIMEOUT
)
87 Object
= NextWaitBlock
->Object
;
89 /* Check if this is a mutant */
90 if ((Object
->Header
.Type
== MutantObject
) &&
91 (Object
->Header
.SignalState
<= 0) &&
92 (WaitThread
== Object
->OwnerThread
))
94 /* It's a signaled mutant */
96 else if (Object
->Header
.SignalState
<= 0)
98 /* Skip the unwaiting */
103 /* Go to the next Wait block */
104 NextWaitBlock
= NextWaitBlock
->NextWaitBlock
;
107 /* All the objects are signaled, we can satisfy */
108 WaitEntry
= WaitEntry
->Blink
;
109 KiWaitSatisfyAll(CurrentWaitBlock
);
112 /* All waits satisfied, unwait the thread */
113 KiAbortWaitThread(WaitThread
, CurrentWaitBlock
->WaitKey
, Increment
);
117 WaitEntry
= WaitEntry
->Flink
;
121 /* Must be called with the dispatcher lock held */
124 KiAbortWaitThread(IN PKTHREAD Thread
,
125 IN NTSTATUS WaitStatus
,
126 IN KPRIORITY Increment
)
128 PKWAIT_BLOCK WaitBlock
;
132 /* Update wait status */
133 Thread
->WaitStatus
|= WaitStatus
;
135 /* Remove the Wait Blocks from the list */
136 WaitBlock
= Thread
->WaitBlockList
;
140 RemoveEntryList(&WaitBlock
->WaitListEntry
);
142 /* Go to the next one */
143 WaitBlock
= WaitBlock
->NextWaitBlock
;
144 } while (WaitBlock
!= Thread
->WaitBlockList
);
146 /* Remove the thread from the wait list! */
147 if (Thread
->WaitListEntry
.Flink
) RemoveEntryList(&Thread
->WaitListEntry
);
149 /* Check if there's a Thread Timer */
150 Timer
= &Thread
->Timer
;
151 if (Timer
->Header
.Inserted
)
153 /* Remove the timer */
154 Timer
->Header
.Inserted
= FALSE
;
155 RemoveEntryList(&Timer
->TimerListEntry
);
156 //KiRemoveTimer(Timer);
159 /* Increment the Queue's active threads */
160 if (Thread
->Queue
) Thread
->Queue
->CurrentCount
++;
162 /* Check if this is a non-RT thread */
163 if (Thread
->Priority
< LOW_REALTIME_PRIORITY
)
165 /* Check if boosting is enabled and we can boost */
166 if (!(Thread
->DisableBoost
) && !(Thread
->PriorityDecrement
))
168 /* We can boost, so calculate the new priority */
169 NewPriority
= Thread
->BasePriority
+ Increment
;
170 if (NewPriority
> Thread
->Priority
)
172 /* Make sure the new priority wouldn't push the thread to RT */
173 if (NewPriority
>= LOW_REALTIME_PRIORITY
)
175 /* Set it just before the RT zone */
176 Thread
->Priority
= LOW_REALTIME_PRIORITY
- 1;
180 /* Otherwise, set our calculated priority */
181 Thread
->Priority
= NewPriority
;
186 /* Check if this is a high-priority thread */
187 if (Thread
->BasePriority
>= 14)
189 /* It is, simply reset the quantum */
190 Thread
->Quantum
= Thread
->QuantumReset
;
194 /* Otherwise, decrease quantum */
196 if (Thread
->Quantum
<= 0)
198 /* We've went below 0, reset it */
199 Thread
->Quantum
= Thread
->QuantumReset
;
201 /* Apply per-quantum priority decrement */
202 Thread
->Priority
-= (Thread
->PriorityDecrement
+ 1);
203 if (Thread
->Priority
< Thread
->BasePriority
)
205 /* We've went too low, reset it */
206 Thread
->Priority
= Thread
->BasePriority
;
209 /* Delete per-quantum decrement */
210 Thread
->PriorityDecrement
= 0;
216 /* For real time threads, just reset the quantum */
217 Thread
->Quantum
= Thread
->QuantumReset
;
220 /* Reschedule the Thread */
221 KiReadyThread(Thread
);
226 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex
)
228 /* Increase contention count */
229 FastMutex
->Contention
++;
231 /* Wait for the event */
232 KeWaitForSingleObject(&FastMutex
->Gate
,
241 KiExitDispatcher(IN KIRQL OldIrql
)
243 /* Check if it's the idle thread */
244 if (!(KeIsExecutingDpc()) &&
245 (OldIrql
< DISPATCH_LEVEL
) &&
246 (KeGetCurrentThread()) &&
247 (KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread
))
249 /* Dispatch a new thread */
250 KiDispatchThreadNoLock(Ready
);
254 /* Otherwise just release the lock */
255 KeReleaseDispatcherDatabaseLockFromDpcLevel();
258 /* Lower irql back */
259 KeLowerIrql(OldIrql
);
262 /* PUBLIC FUNCTIONS **********************************************************/
269 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode
,
270 IN BOOLEAN Alertable
,
271 IN PLARGE_INTEGER Interval OPTIONAL
)
273 PKWAIT_BLOCK TimerWaitBlock
;
275 PKTHREAD CurrentThread
= KeGetCurrentThread();
276 NTSTATUS WaitStatus
= STATUS_SUCCESS
;
278 PLARGE_INTEGER OriginalDueTime
= Interval
;
279 LARGE_INTEGER DueTime
, NewDueTime
;
281 /* Check if the lock is already held */
282 if (CurrentThread
->WaitNext
)
284 /* Lock is held, disable Wait Next */
285 CurrentThread
->WaitNext
= FALSE
;
289 /* Lock not held, acquire it */
290 CurrentThread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
293 /* Use built-in Wait block */
294 TimerWaitBlock
= &CurrentThread
->WaitBlock
[TIMER_WAIT_BLOCK
];
296 /* Start Wait Loop */
299 /* Check if a kernel APC is pending and we're below APC_LEVEL */
300 if ((CurrentThread
->ApcState
.KernelApcPending
) &&
301 !(CurrentThread
->SpecialApcDisable
) &&
302 (CurrentThread
->WaitIrql
< APC_LEVEL
))
304 /* Unlock the dispatcher */
305 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);
309 /* Check if we can do an alertable wait, if requested */
310 KiCheckAlertability();
312 /* Check if we can swap the thread's stack */
313 CurrentThread
->WaitListEntry
.Flink
= NULL
;
314 KiCheckThreadStackSwap(WaitMode
, CurrentThread
, Swappable
);
317 CurrentThread
->WaitStatus
= STATUS_WAIT_0
;
320 ThreadTimer
= &CurrentThread
->Timer
;
322 /* Setup the Wait Block */
323 CurrentThread
->WaitBlockList
= TimerWaitBlock
;
324 TimerWaitBlock
->NextWaitBlock
= TimerWaitBlock
;
326 /* Link the timer to this Wait Block */
327 ThreadTimer
->Header
.WaitListHead
.Flink
=
328 &TimerWaitBlock
->WaitListEntry
;
329 ThreadTimer
->Header
.WaitListHead
.Blink
=
330 &TimerWaitBlock
->WaitListEntry
;
332 /* Insert the Timer into the Timer Lists and enable it */
333 if (!KiInsertTimer(ThreadTimer
, *Interval
))
335 /* FIXME: We should find a new ready thread */
336 WaitStatus
= STATUS_SUCCESS
;
341 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
343 /* Handle Kernel Queues */
344 if (CurrentThread
->Queue
) KiWakeQueue(CurrentThread
->Queue
);
346 /* Setup the wait information */
347 CurrentThread
->Alertable
= Alertable
;
348 CurrentThread
->WaitMode
= WaitMode
;
349 CurrentThread
->WaitReason
= DelayExecution
;
350 CurrentThread
->WaitTime
= ((PLARGE_INTEGER
)&KeTickCount
)->LowPart
;
351 CurrentThread
->State
= Waiting
;
353 /* Find a new thread to run */
354 KiAddThreadToWaitList(CurrentThread
, Swappable
);
355 WaitStatus
= KiSwapThread();
356 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
358 /* Check if we were executing an APC or if we timed out */
359 if (WaitStatus
!= STATUS_KERNEL_APC
)
361 /* This is a good thing */
362 if (WaitStatus
== STATUS_TIMEOUT
) WaitStatus
= STATUS_SUCCESS
;
368 /* Recalculate due times */
369 Interval
= KiRecalculateDueTime(OriginalDueTime
,
374 /* Acquire again the lock */
375 CurrentThread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
378 /* Release the Lock, we are done */
379 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);
388 KeWaitForSingleObject(IN PVOID Object
,
389 IN KWAIT_REASON WaitReason
,
390 IN KPROCESSOR_MODE WaitMode
,
391 IN BOOLEAN Alertable
,
392 IN PLARGE_INTEGER Timeout OPTIONAL
)
394 PKMUTANT CurrentObject
;
395 PKWAIT_BLOCK WaitBlock
;
396 PKWAIT_BLOCK TimerWaitBlock
;
398 PKTHREAD CurrentThread
= KeGetCurrentThread();
399 NTSTATUS WaitStatus
= STATUS_SUCCESS
;
401 LARGE_INTEGER DueTime
, NewDueTime
;
402 PLARGE_INTEGER OriginalDueTime
= Timeout
;
404 /* Check if the lock is already held */
405 if (CurrentThread
->WaitNext
)
407 /* Lock is held, disable Wait Next */
408 CurrentThread
->WaitNext
= FALSE
;
412 /* Lock not held, acquire it */
413 CurrentThread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
416 /* Start the actual Loop */
417 WaitBlock
= &CurrentThread
->WaitBlock
[0];
420 /* Check if a kernel APC is pending and we're below APC_LEVEL */
421 if ((CurrentThread
->ApcState
.KernelApcPending
) &&
422 !(CurrentThread
->SpecialApcDisable
) &&
423 (CurrentThread
->WaitIrql
< APC_LEVEL
))
425 /* Unlock the dispatcher */
426 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);
430 /* Set default status */
431 CurrentThread
->WaitStatus
= STATUS_WAIT_0
;
433 /* Get the Current Object */
434 CurrentObject
= (PKMUTANT
)Object
;
436 /* Check if it's a mutant */
437 if (CurrentObject
->Header
.Type
== MutantObject
)
439 /* Check its signal state or if we own it */
440 if ((CurrentObject
->Header
.SignalState
> 0) ||
441 (CurrentThread
== CurrentObject
->OwnerThread
))
443 /* Just unwait this guy and exit */
444 if (CurrentObject
->Header
.SignalState
!= (LONG
)MINLONG
)
446 /* It has a normal signal state. Unwait and return */
447 KiSatisfyMutantWait(CurrentObject
, CurrentThread
);
448 WaitStatus
= CurrentThread
->WaitStatus
;
453 /* Raise an exception (see wasm.ru) */
454 KeReleaseDispatcherDatabaseLock(CurrentThread
->
456 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
460 else if (CurrentObject
->Header
.SignalState
> 0)
462 /* Another satisfied object */
463 KiSatisfyNonMutantWait(CurrentObject
, CurrentThread
);
464 WaitStatus
= STATUS_WAIT_0
;
468 /* Append wait block to the KTHREAD wait block list */
469 CurrentThread
->WaitBlockList
= WaitBlock
;
471 /* Set up the Wait Block */
472 WaitBlock
->Object
= CurrentObject
;
473 WaitBlock
->WaitKey
= (USHORT
)(STATUS_SUCCESS
);
474 WaitBlock
->WaitType
= WaitAny
;
476 /* Make sure we can satisfy the Alertable request */
477 KiCheckAlertability();
479 /* Check if we can swap the thread's stack */
480 CurrentThread
->WaitListEntry
.Flink
= NULL
;
481 KiCheckThreadStackSwap(WaitMode
, CurrentThread
, Swappable
);
483 /* Enable the Timeout Timer if there was any specified */
486 /* Fail if the timeout interval is actually 0 */
487 if (!Timeout
->QuadPart
)
489 /* Return a timeout */
490 WaitStatus
= STATUS_TIMEOUT
;
494 /* Point to Timer Wait Block and Thread Timer */
495 TimerWaitBlock
= &CurrentThread
->WaitBlock
[TIMER_WAIT_BLOCK
];
496 ThreadTimer
= &CurrentThread
->Timer
;
498 /* Connect the Timer Wait Block */
499 WaitBlock
->NextWaitBlock
= TimerWaitBlock
;
501 /* Set up the Timer Wait Block */
502 TimerWaitBlock
->NextWaitBlock
= WaitBlock
;
504 /* Link the timer to this Wait Block */
505 ThreadTimer
->Header
.WaitListHead
.Flink
=
506 &TimerWaitBlock
->WaitListEntry
;
507 ThreadTimer
->Header
.WaitListHead
.Blink
=
508 &TimerWaitBlock
->WaitListEntry
;
510 /* Insert the Timer into the Timer Lists and enable it */
511 if (!KiInsertTimer(ThreadTimer
, *Timeout
))
513 /* Return a timeout if we couldn't insert the timer */
514 WaitStatus
= STATUS_TIMEOUT
;
518 /* Set the current due time */
519 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
523 /* No timer block, so just set our wait block as next */
524 WaitBlock
->NextWaitBlock
= WaitBlock
;
527 /* Link the Object to this Wait Block */
528 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
529 &WaitBlock
->WaitListEntry
);
531 /* Handle Kernel Queues */
532 if (CurrentThread
->Queue
) KiWakeQueue(CurrentThread
->Queue
);
534 /* Setup the wait information */
535 CurrentThread
->Alertable
= Alertable
;
536 CurrentThread
->WaitMode
= WaitMode
;
537 CurrentThread
->WaitReason
= WaitReason
;
538 CurrentThread
->WaitTime
= ((PLARGE_INTEGER
)&KeTickCount
)->LowPart
;
539 CurrentThread
->State
= Waiting
;
541 /* Find a new thread to run */
542 KiAddThreadToWaitList(CurrentThread
, Swappable
);
543 WaitStatus
= KiSwapThread();
544 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
546 /* Check if we were executing an APC */
547 if (WaitStatus
!= STATUS_KERNEL_APC
) return WaitStatus
;
549 /* Check if we had a timeout */
552 /* Recalculate due times */
553 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
559 /* Acquire again the lock */
560 CurrentThread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
563 /* Release the Lock, we are done */
564 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);
568 /* Adjust the Quantum */
569 KiAdjustQuantumThread(CurrentThread
);
571 /* Release & Return */
572 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);
581 KeWaitForMultipleObjects(IN ULONG Count
,
583 IN WAIT_TYPE WaitType
,
584 IN KWAIT_REASON WaitReason
,
585 IN KPROCESSOR_MODE WaitMode
,
586 IN BOOLEAN Alertable
,
587 IN PLARGE_INTEGER Timeout OPTIONAL
,
588 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL
)
590 PKMUTANT CurrentObject
;
591 PKWAIT_BLOCK WaitBlock
;
592 PKWAIT_BLOCK TimerWaitBlock
;
594 PKTHREAD CurrentThread
= KeGetCurrentThread();
595 ULONG AllObjectsSignaled
;
597 NTSTATUS WaitStatus
= STATUS_SUCCESS
;
599 PLARGE_INTEGER OriginalDueTime
= Timeout
;
600 LARGE_INTEGER DueTime
, NewDueTime
;
602 /* Set the Current Thread */
603 CurrentThread
= KeGetCurrentThread();
605 /* Check if the lock is already held */
606 if (CurrentThread
->WaitNext
)
608 /* Lock is held, disable Wait Next */
609 CurrentThread
->WaitNext
= FALSE
;
613 /* Lock not held, acquire it */
614 CurrentThread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
617 /* Make sure the Wait Count is valid */
620 /* Check in regards to the Thread Object Limit */
621 if (Count
> THREAD_WAIT_OBJECTS
)
624 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
627 /* Use the Thread's Wait Block */
628 WaitBlockArray
= &CurrentThread
->WaitBlock
[0];
632 /* Using our own Block Array, so check with the System Object Limit */
633 if (Count
> MAXIMUM_WAIT_OBJECTS
)
636 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED
);
640 /* Start the actual Loop */
643 /* Check if a kernel APC is pending and we're below APC_LEVEL */
644 if ((CurrentThread
->ApcState
.KernelApcPending
) &&
645 !(CurrentThread
->SpecialApcDisable
) &&
646 (CurrentThread
->WaitIrql
< APC_LEVEL
))
648 /* Unlock the dispatcher */
649 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);
653 /* Append wait block to the KTHREAD wait block list */
654 CurrentThread
->WaitBlockList
= WaitBlock
= WaitBlockArray
;
656 /* Set default wait status */
657 CurrentThread
->WaitStatus
= STATUS_WAIT_0
;
659 /* Check if the wait is (already) satisfied */
660 AllObjectsSignaled
= TRUE
;
662 /* First, we'll try to satisfy the wait directly */
663 for (WaitIndex
= 0; WaitIndex
< Count
; WaitIndex
++)
665 /* Get the Current Object */
666 CurrentObject
= (PKMUTANT
)Object
[WaitIndex
];
668 /* Check the type of wait */
669 if (WaitType
== WaitAny
)
671 /* Check if the Object is a mutant */
672 if (CurrentObject
->Header
.Type
== MutantObject
)
674 /* Check if it's signaled */
675 if ((CurrentObject
->Header
.SignalState
> 0) ||
676 (CurrentThread
== CurrentObject
->OwnerThread
))
678 /* This is a Wait Any, so unwait this and exit */
679 if (CurrentObject
->Header
.SignalState
!=
682 /* Normal signal state, unwait it and return */
683 KiSatisfyMutantWait(CurrentObject
,
685 WaitStatus
= CurrentThread
->WaitStatus
|
691 /* Raise an exception (see wasm.ru) */
692 KeReleaseDispatcherDatabaseLock(CurrentThread
->
694 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
698 else if (CurrentObject
->Header
.SignalState
> 0)
700 /* Another signaled object, unwait and return */
701 KiSatisfyNonMutantWait(CurrentObject
, CurrentThread
);
702 WaitStatus
= WaitIndex
;
708 /* Check if we're dealing with a mutant again */
709 if (CurrentObject
->Header
.Type
== MutantObject
)
711 /* Check if it has an invalid count */
712 if ((CurrentThread
== CurrentObject
->OwnerThread
) &&
713 (CurrentObject
->Header
.SignalState
== MINLONG
))
715 /* Raise an exception */
716 KeReleaseDispatcherDatabaseLock(CurrentThread
->
718 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED
);
720 else if ((CurrentObject
->Header
.SignalState
<= 0) &&
721 (CurrentThread
!= CurrentObject
->OwnerThread
))
723 /* We don't own it, can't satisfy the wait */
724 AllObjectsSignaled
= FALSE
;
727 else if (CurrentObject
->Header
.SignalState
<= 0)
729 /* Not signaled, can't satisfy */
730 AllObjectsSignaled
= FALSE
;
734 /* Set up a Wait Block for this Object */
735 WaitBlock
->Object
= CurrentObject
;
736 WaitBlock
->Thread
= CurrentThread
;
737 WaitBlock
->WaitKey
= (USHORT
)WaitIndex
;
738 WaitBlock
->WaitType
= (USHORT
)WaitType
;
739 WaitBlock
->NextWaitBlock
= WaitBlock
+ 1;
741 /* Move to the next Wait Block */
742 WaitBlock
= WaitBlock
->NextWaitBlock
;
745 /* Return to the Root Wait Block */
747 WaitBlock
->NextWaitBlock
= WaitBlockArray
;
749 /* Check if this is a Wait All and all the objects are signaled */
750 if ((WaitType
== WaitAll
) && (AllObjectsSignaled
))
752 /* Return to the Root Wait Block */
753 WaitBlock
= CurrentThread
->WaitBlockList
;
755 /* Satisfy their Waits and return to the caller */
756 KiWaitSatisfyAll(WaitBlock
);
757 WaitStatus
= CurrentThread
->WaitStatus
;
761 /* Make sure we can satisfy the Alertable request */
762 KiCheckAlertability();
764 /* Check if we can swap the thread's stack */
765 CurrentThread
->WaitListEntry
.Flink
= NULL
;
766 KiCheckThreadStackSwap(WaitMode
, CurrentThread
, Swappable
);
768 /* Enable the Timeout Timer if there was any specified */
771 /* Make sure the timeout interval isn't actually 0 */
772 if (!Timeout
->QuadPart
)
774 /* Return a timeout */
775 WaitStatus
= STATUS_TIMEOUT
;
779 /* Point to Timer Wait Block and Thread Timer */
780 TimerWaitBlock
= &CurrentThread
->WaitBlock
[TIMER_WAIT_BLOCK
];
781 ThreadTimer
= &CurrentThread
->Timer
;
783 /* Connect the Timer Wait Block */
784 WaitBlock
->NextWaitBlock
= TimerWaitBlock
;
786 /* Set up the Timer Wait Block */
787 TimerWaitBlock
->NextWaitBlock
= WaitBlockArray
;
789 /* Initialize the list head */
790 InitializeListHead(&ThreadTimer
->Header
.WaitListHead
);
792 /* Insert the Timer into the Timer Lists and enable it */
793 if (!KiInsertTimer(ThreadTimer
, *Timeout
))
795 /* Return a timeout if we couldn't insert the timer */
796 WaitStatus
= STATUS_TIMEOUT
;
800 /* Set the current due time */
801 DueTime
.QuadPart
= ThreadTimer
->DueTime
.QuadPart
;
804 /* Insert into Object's Wait List*/
805 WaitBlock
= CurrentThread
->WaitBlockList
;
808 /* Get the Current Object */
809 CurrentObject
= WaitBlock
->Object
;
811 /* Link the Object to this Wait Block */
812 InsertTailList(&CurrentObject
->Header
.WaitListHead
,
813 &WaitBlock
->WaitListEntry
);
815 /* Move to the next Wait Block */
816 WaitBlock
= WaitBlock
->NextWaitBlock
;
817 } while (WaitBlock
!= WaitBlockArray
);
819 /* Handle Kernel Queues */
820 if (CurrentThread
->Queue
) KiWakeQueue(CurrentThread
->Queue
);
822 /* Setup the wait information */
823 CurrentThread
->Alertable
= Alertable
;
824 CurrentThread
->WaitMode
= WaitMode
;
825 CurrentThread
->WaitReason
= WaitReason
;
826 CurrentThread
->WaitTime
= ((PLARGE_INTEGER
)&KeTickCount
)->LowPart
;
827 CurrentThread
->State
= Waiting
;
829 /* Find a new thread to run */
830 KiAddThreadToWaitList(CurrentThread
, Swappable
);
831 WaitStatus
= KiSwapThread();
832 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
834 /* Check if we were executing an APC */
835 if (WaitStatus
!= STATUS_KERNEL_APC
) return WaitStatus
;
837 /* Check if we had a timeout */
840 /* Recalculate due times */
841 Timeout
= KiRecalculateDueTime(OriginalDueTime
,
846 /* Acquire again the lock */
847 CurrentThread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
851 /* Release the Lock, we are done */
852 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);
856 /* Adjust the Quantum */
857 KiAdjustQuantumThread(CurrentThread
);
859 /* Release & Return */
860 KeReleaseDispatcherDatabaseLock(CurrentThread
->WaitIrql
);