2e9c1c884a9ad4cadd353808447d91cd3c4219be
[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 NTSTATUS 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 VOID
121 FASTCALL
122 KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
123 {
124 ULONG BitsToRemove, BitsToAdd;
125 LONG OldValue, NewValue;
126
127 /* We depend on these bits being just right */
128 C_ASSERT((GM_LOCK_WAITER_WOKEN * 2) == GM_LOCK_WAITER_INC);
129
130 /* Increase the contention count */
131 GuardedMutex->Contention++;
132
133 /* Start by unlocking the Guarded Mutex */
134 BitsToRemove = GM_LOCK_BIT;
135 BitsToAdd = GM_LOCK_WAITER_INC;
136
137 /* Start change loop */
138 for (;;)
139 {
140 /* Loop sanity checks */
141 ASSERT((BitsToRemove == GM_LOCK_BIT) ||
142 (BitsToRemove == (GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN)));
143 ASSERT((BitsToAdd == GM_LOCK_WAITER_INC) ||
144 (BitsToAdd == GM_LOCK_WAITER_WOKEN));
145
146 /* Get the Count Bits */
147 OldValue = GuardedMutex->Count;
148
149 /* Start internal bit change loop */
150 for (;;)
151 {
152 /* Check if the Guarded Mutex is locked */
153 if (OldValue & GM_LOCK_BIT)
154 {
155 /* Sanity check */
156 ASSERT((BitsToRemove == GM_LOCK_BIT) ||
157 ((OldValue & GM_LOCK_WAITER_WOKEN) != 0));
158
159 /* Unlock it by removing the Lock Bit */
160 NewValue = OldValue ^ BitsToRemove;
161 NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
162 NewValue,
163 OldValue);
164 if (NewValue == OldValue) return;
165 }
166 else
167 {
168 /* The Guarded Mutex isn't locked, so simply set the bits */
169 NewValue = OldValue + BitsToAdd;
170 NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
171 NewValue,
172 OldValue);
173 if (NewValue == OldValue) break;
174 }
175
176 /* Old value changed, loop again */
177 OldValue = NewValue;
178 }
179
180 /* Now we have to wait for it */
181 KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode);
182 ASSERT((GuardedMutex->Count & GM_LOCK_WAITER_WOKEN) != 0);
183
184 /* Ok, the wait is done, so set the new bits */
185 BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN;
186 BitsToAdd = GM_LOCK_WAITER_WOKEN;
187 }
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(KeGetCurrentIrql() == 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 /* Lock the PRCB */
229 KiAcquirePrcbLock(Prcb);
230
231 /* Get the next and current threads now */
232 NextThread = Prcb->NextThread;
233 Thread = Prcb->CurrentThread;
234
235 /* Set current thread's swap busy to true */
236 KiSetThreadSwapBusy(Thread);
237
238 /* Switch threads in PRCB */
239 Prcb->NextThread = NULL;
240 Prcb->CurrentThread = NextThread;
241
242 /* Set thread to running */
243 NextThread->State = Running;
244
245 /* Queue it on the ready lists */
246 KxQueueReadyThread(Thread, Prcb);
247
248 /* Set wait IRQL */
249 Thread->WaitIrql = OldIrql;
250
251 /* Swap threads and check if APCs were pending */
252 PendingApc = KiSwapContext(Thread, NextThread);
253 if (PendingApc)
254 {
255 /* Lower only to APC */
256 KeLowerIrql(APC_LEVEL);
257
258 /* Deliver APCs */
259 KiDeliverApc(KernelMode, NULL, NULL);
260 ASSERT(OldIrql == PASSIVE_LEVEL);
261 }
262
263 /* Lower IRQl back */
264 Quickie:
265 KeLowerIrql(OldIrql);
266 }
267
268 /* PUBLIC FUNCTIONS **********************************************************/
269
270 /*
271 * @implemented
272 */
273 NTSTATUS
274 NTAPI
275 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
276 IN BOOLEAN Alertable,
277 IN PLARGE_INTEGER Interval OPTIONAL)
278 {
279 PKTIMER Timer;
280 PKWAIT_BLOCK TimerBlock;
281 PKTHREAD Thread = KeGetCurrentThread();
282 NTSTATUS WaitStatus;
283 BOOLEAN Swappable;
284 PLARGE_INTEGER OriginalDueTime;
285 LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
286 ULONG Hand = 0;
287
288 /* If this is a user-mode wait of 0 seconds, yield execution */
289 if (!(Interval->QuadPart) && (WaitMode != KernelMode))
290 {
291 /* Make sure the wait isn't alertable or interrupting an APC */
292 if (!(Alertable) && !(Thread->ApcState.UserApcPending))
293 {
294 /* Yield execution */
295 NtYieldExecution();
296 }
297 }
298
299 /* Setup the original time and timer/wait blocks */
300 OriginalDueTime = Interval;
301 Timer = &Thread->Timer;
302 TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
303
304 /* Check if the lock is already held */
305 if (!Thread->WaitNext) goto WaitStart;
306
307 /* Otherwise, we already have the lock, so initialize the wait */
308 Thread->WaitNext = FALSE;
309 KxDelayThreadWait();
310
311 /* Start wait loop */
312 for (;;)
313 {
314 /* Disable pre-emption */
315 Thread->Preempted = FALSE;
316
317 /* Check if a kernel APC is pending and we're below APC_LEVEL */
318 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
319 (Thread->WaitIrql < APC_LEVEL))
320 {
321 /* Unlock the dispatcher */
322 KiReleaseDispatcherLock(Thread->WaitIrql);
323 }
324 else
325 {
326 /* Check if we have to bail out due to an alerted state */
327 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
328 if (WaitStatus != STATUS_WAIT_0) break;
329
330 /* Check if the timer expired */
331 InterruptTime.QuadPart = KeQueryInterruptTime();
332 if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart)
333 {
334 /* It did, so we don't need to wait */
335 goto NoWait;
336 }
337
338 /* It didn't, so activate it */
339 Timer->Header.Inserted = TRUE;
340
341 /* Handle Kernel Queues */
342 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
343
344 /* Setup the wait information */
345 Thread->State = Waiting;
346
347 /* Add the thread to the wait list */
348 KiAddThreadToWaitList(Thread, Swappable);
349
350 /* Insert the timer and swap the thread */
351 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
352 KiSetThreadSwapBusy(Thread);
353 KxInsertTimer(Timer, Hand);
354 WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
355
356 /* Check if were swapped ok */
357 if (WaitStatus != STATUS_KERNEL_APC)
358 {
359 /* This is a good thing */
360 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
361
362 /* Return Status */
363 return WaitStatus;
364 }
365
366 /* Recalculate due times */
367 Interval = KiRecalculateDueTime(OriginalDueTime,
368 &DueTime,
369 &NewDueTime);
370 }
371
372 WaitStart:
373 /* Setup a new wait */
374 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
375 KxDelayThreadWait();
376 KiAcquireDispatcherLockAtDpcLevel();
377 }
378
379 /* We're done! */
380 KiReleaseDispatcherLock(Thread->WaitIrql);
381 return WaitStatus;
382
383 NoWait:
384 /* There was nothing to wait for. Did we have a wait interval? */
385 if (!Interval->QuadPart)
386 {
387 /* Unlock the dispatcher and do a yield */
388 KiReleaseDispatcherLock(Thread->WaitIrql);
389 return NtYieldExecution();
390 }
391
392 /* Unlock the dispatcher and adjust the quantum for a no-wait */
393 KiReleaseDispatcherLockFromDpcLevel();
394 KiAdjustQuantumThread(Thread);
395 return STATUS_SUCCESS;
396 }
397
398 /*
399 * @implemented
400 */
401 NTSTATUS
402 NTAPI
403 KeWaitForSingleObject(IN PVOID Object,
404 IN KWAIT_REASON WaitReason,
405 IN KPROCESSOR_MODE WaitMode,
406 IN BOOLEAN Alertable,
407 IN PLARGE_INTEGER Timeout OPTIONAL)
408 {
409 PKTHREAD Thread = KeGetCurrentThread();
410 PKMUTANT CurrentObject = (PKMUTANT)Object;
411 PKWAIT_BLOCK WaitBlock = &Thread->WaitBlock[0];
412 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
413 PKTIMER Timer = &Thread->Timer;
414 NTSTATUS WaitStatus;
415 BOOLEAN Swappable;
416 LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
417 PLARGE_INTEGER OriginalDueTime = Timeout;
418 ULONG Hand = 0;
419
420 /* Check if the lock is already held */
421 if (!Thread->WaitNext) goto WaitStart;
422
423 /* Otherwise, we already have the lock, so initialize the wait */
424 Thread->WaitNext = FALSE;
425 KxSingleThreadWait();
426
427 /* Start wait loop */
428 for (;;)
429 {
430 /* Disable pre-emption */
431 Thread->Preempted = FALSE;
432
433 /* Check if a kernel APC is pending and we're below APC_LEVEL */
434 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
435 (Thread->WaitIrql < APC_LEVEL))
436 {
437 /* Unlock the dispatcher */
438 KiReleaseDispatcherLock(Thread->WaitIrql);
439 }
440 else
441 {
442 /* Sanity check */
443 ASSERT(CurrentObject->Header.Type != QueueObject);
444
445 /* Check if it's a mutant */
446 if (CurrentObject->Header.Type == MutantObject)
447 {
448 /* Check its signal state or if we own it */
449 if ((CurrentObject->Header.SignalState > 0) ||
450 (Thread == CurrentObject->OwnerThread))
451 {
452 /* Just unwait this guy and exit */
453 if (CurrentObject->Header.SignalState != (LONG)MINLONG)
454 {
455 /* It has a normal signal state. Unwait and return */
456 KiSatisfyMutantWait(CurrentObject, Thread);
457 WaitStatus = Thread->WaitStatus;
458 goto DontWait;
459 }
460 else
461 {
462 /* Raise an exception */
463 KiReleaseDispatcherLock(Thread->WaitIrql);
464 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
465 }
466 }
467 }
468 else if (CurrentObject->Header.SignalState > 0)
469 {
470 /* Another satisfied object */
471 KiSatisfyNonMutantWait(CurrentObject);
472 WaitStatus = STATUS_WAIT_0;
473 goto DontWait;
474 }
475
476 /* Make sure we can satisfy the Alertable request */
477 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
478 if (WaitStatus != STATUS_WAIT_0) break;
479
480 /* Enable the Timeout Timer if there was any specified */
481 if (Timeout)
482 {
483 /* Check if the timer expired */
484 InterruptTime.QuadPart = KeQueryInterruptTime();
485 if ((ULONGLONG)InterruptTime.QuadPart >=
486 Timer->DueTime.QuadPart)
487 {
488 /* It did, so we don't need to wait */
489 WaitStatus = STATUS_TIMEOUT;
490 goto DontWait;
491 }
492
493 /* It didn't, so activate it */
494 Timer->Header.Inserted = TRUE;
495 }
496
497 /* Link the Object to this Wait Block */
498 InsertTailList(&CurrentObject->Header.WaitListHead,
499 &WaitBlock->WaitListEntry);
500
501 /* Handle Kernel Queues */
502 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
503
504 /* Setup the wait information */
505 Thread->State = Waiting;
506
507 /* Add the thread to the wait list */
508 KiAddThreadToWaitList(Thread, Swappable);
509
510 /* Activate thread swap */
511 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
512 KiSetThreadSwapBusy(Thread);
513
514 /* Check if we have a timer */
515 if (Timeout)
516 {
517 /* Insert it */
518 KxInsertTimer(Timer, Hand);
519 }
520 else
521 {
522 /* Otherwise, unlock the dispatcher */
523 KiReleaseDispatcherLockFromDpcLevel();
524 }
525
526 /* Do the actual swap */
527 WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
528
529 /* Check if we were executing an APC */
530 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
531
532 /* Check if we had a timeout */
533 if (Timeout)
534 {
535 /* Recalculate due times */
536 Timeout = KiRecalculateDueTime(OriginalDueTime,
537 &DueTime,
538 &NewDueTime);
539 }
540 }
541 WaitStart:
542 /* Setup a new wait */
543 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
544 KxSingleThreadWait();
545 KiAcquireDispatcherLockAtDpcLevel();
546 }
547
548 /* Wait complete */
549 KiReleaseDispatcherLock(Thread->WaitIrql);
550 return WaitStatus;
551
552 DontWait:
553 /* Release dispatcher lock but maintain high IRQL */
554 KiReleaseDispatcherLockFromDpcLevel();
555
556 /* Adjust the Quantum and return the wait status */
557 KiAdjustQuantumThread(Thread);
558 return WaitStatus;
559 }
560
561 /*
562 * @implemented
563 */
564 NTSTATUS
565 NTAPI
566 KeWaitForMultipleObjects(IN ULONG Count,
567 IN PVOID Object[],
568 IN WAIT_TYPE WaitType,
569 IN KWAIT_REASON WaitReason,
570 IN KPROCESSOR_MODE WaitMode,
571 IN BOOLEAN Alertable,
572 IN PLARGE_INTEGER Timeout OPTIONAL,
573 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
574 {
575 PKMUTANT CurrentObject;
576 PKWAIT_BLOCK WaitBlock;
577 PKTHREAD Thread = KeGetCurrentThread();
578 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
579 PKTIMER Timer = &Thread->Timer;
580 NTSTATUS WaitStatus = STATUS_SUCCESS;
581 BOOLEAN Swappable;
582 PLARGE_INTEGER OriginalDueTime = Timeout;
583 LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
584 ULONG Index, Hand = 0;
585
586 /* Make sure the Wait Count is valid */
587 if (!WaitBlockArray)
588 {
589 /* Check in regards to the Thread Object Limit */
590 if (Count > THREAD_WAIT_OBJECTS)
591 {
592 /* Bugcheck */
593 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
594 }
595
596 /* Use the Thread's Wait Block */
597 WaitBlockArray = &Thread->WaitBlock[0];
598 }
599 else
600 {
601 /* Using our own Block Array, so check with the System Object Limit */
602 if (Count > MAXIMUM_WAIT_OBJECTS)
603 {
604 /* Bugcheck */
605 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
606 }
607 }
608
609 /* Sanity check */
610 ASSERT(Count != 0);
611
612 /* Check if the lock is already held */
613 if (!Thread->WaitNext) goto WaitStart;
614
615 /* Otherwise, we already have the lock, so initialize the wait */
616 Thread->WaitNext = FALSE;
617 /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */
618 /* uses (and modifies some of) the following local */
619 /* variables: */
620 /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */
621 /* If it looks like this code doesn't actually wait for any objects */
622 /* at all, it's because the setup is done by that macro. */
623 KxMultiThreadWait();
624
625 /* Start wait loop */
626 for (;;)
627 {
628 /* Disable pre-emption */
629 Thread->Preempted = FALSE;
630
631 /* Check if a kernel APC is pending and we're below APC_LEVEL */
632 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
633 (Thread->WaitIrql < APC_LEVEL))
634 {
635 /* Unlock the dispatcher */
636 KiReleaseDispatcherLock(Thread->WaitIrql);
637 }
638 else
639 {
640 /* Check what kind of wait this is */
641 Index = 0;
642 if (WaitType == WaitAny)
643 {
644 /* Loop blocks */
645 do
646 {
647 /* Get the Current Object */
648 CurrentObject = (PKMUTANT)Object[Index];
649 ASSERT(CurrentObject->Header.Type != QueueObject);
650
651 /* Check if the Object is a mutant */
652 if (CurrentObject->Header.Type == MutantObject)
653 {
654 /* Check if it's signaled */
655 if ((CurrentObject->Header.SignalState > 0) ||
656 (Thread == CurrentObject->OwnerThread))
657 {
658 /* This is a Wait Any, so unwait this and exit */
659 if (CurrentObject->Header.SignalState !=
660 (LONG)MINLONG)
661 {
662 /* Normal signal state, unwait it and return */
663 KiSatisfyMutantWait(CurrentObject, Thread);
664 WaitStatus = Thread->WaitStatus | Index;
665 goto DontWait;
666 }
667 else
668 {
669 /* Raise an exception (see wasm.ru) */
670 KiReleaseDispatcherLock(Thread->WaitIrql);
671 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
672 }
673 }
674 }
675 else if (CurrentObject->Header.SignalState > 0)
676 {
677 /* Another signaled object, unwait and return */
678 KiSatisfyNonMutantWait(CurrentObject);
679 WaitStatus = Index;
680 goto DontWait;
681 }
682
683 /* Go to the next block */
684 Index++;
685 } while (Index < Count);
686 }
687 else
688 {
689 /* Loop blocks */
690 do
691 {
692 /* Get the Current Object */
693 CurrentObject = (PKMUTANT)Object[Index];
694 ASSERT(CurrentObject->Header.Type != QueueObject);
695
696 /* Check if we're dealing with a mutant again */
697 if (CurrentObject->Header.Type == MutantObject)
698 {
699 /* Check if it has an invalid count */
700 if ((Thread == CurrentObject->OwnerThread) &&
701 (CurrentObject->Header.SignalState == (LONG)MINLONG))
702 {
703 /* Raise an exception */
704 KiReleaseDispatcherLock(Thread->WaitIrql);
705 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
706 }
707 else if ((CurrentObject->Header.SignalState <= 0) &&
708 (Thread != CurrentObject->OwnerThread))
709 {
710 /* We don't own it, can't satisfy the wait */
711 break;
712 }
713 }
714 else if (CurrentObject->Header.SignalState <= 0)
715 {
716 /* Not signaled, can't satisfy */
717 break;
718 }
719
720 /* Go to the next block */
721 Index++;
722 } while (Index < Count);
723
724 /* Check if we've went through all the objects */
725 if (Index == Count)
726 {
727 /* Loop wait blocks */
728 WaitBlock = WaitBlockArray;
729 do
730 {
731 /* Get the object and satisfy it */
732 CurrentObject = (PKMUTANT)WaitBlock->Object;
733 KiSatisfyObjectWait(CurrentObject, Thread);
734
735 /* Go to the next block */
736 WaitBlock = WaitBlock->NextWaitBlock;
737 } while(WaitBlock != WaitBlockArray);
738
739 /* Set the wait status and get out */
740 WaitStatus = Thread->WaitStatus;
741 goto DontWait;
742 }
743 }
744
745 /* Make sure we can satisfy the Alertable request */
746 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
747 if (WaitStatus != STATUS_WAIT_0) break;
748
749 /* Enable the Timeout Timer if there was any specified */
750 if (Timeout)
751 {
752 /* Check if the timer expired */
753 InterruptTime.QuadPart = KeQueryInterruptTime();
754 if ((ULONGLONG)InterruptTime.QuadPart >=
755 Timer->DueTime.QuadPart)
756 {
757 /* It did, so we don't need to wait */
758 WaitStatus = STATUS_TIMEOUT;
759 goto DontWait;
760 }
761
762 /* It didn't, so activate it */
763 Timer->Header.Inserted = TRUE;
764
765 /* Link the wait blocks */
766 WaitBlock->NextWaitBlock = TimerBlock;
767 }
768
769 /* Insert into Object's Wait List*/
770 WaitBlock = WaitBlockArray;
771 do
772 {
773 /* Get the Current Object */
774 CurrentObject = WaitBlock->Object;
775
776 /* Link the Object to this Wait Block */
777 InsertTailList(&CurrentObject->Header.WaitListHead,
778 &WaitBlock->WaitListEntry);
779
780 /* Move to the next Wait Block */
781 WaitBlock = WaitBlock->NextWaitBlock;
782 } while (WaitBlock != WaitBlockArray);
783
784 /* Handle Kernel Queues */
785 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
786
787 /* Setup the wait information */
788 Thread->State = Waiting;
789
790 /* Add the thread to the wait list */
791 KiAddThreadToWaitList(Thread, Swappable);
792
793 /* Activate thread swap */
794 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
795 KiSetThreadSwapBusy(Thread);
796
797 /* Check if we have a timer */
798 if (Timeout)
799 {
800 /* Insert it */
801 KxInsertTimer(Timer, Hand);
802 }
803 else
804 {
805 /* Otherwise, unlock the dispatcher */
806 KiReleaseDispatcherLockFromDpcLevel();
807 }
808
809 /* Swap the thread */
810 WaitStatus = KiSwapThread(Thread, KeGetCurrentPrcb());
811
812 /* Check if we were executing an APC */
813 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
814
815 /* Check if we had a timeout */
816 if (Timeout)
817 {
818 /* Recalculate due times */
819 Timeout = KiRecalculateDueTime(OriginalDueTime,
820 &DueTime,
821 &NewDueTime);
822 }
823 }
824
825 WaitStart:
826 /* Setup a new wait */
827 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
828 KxMultiThreadWait();
829 KiAcquireDispatcherLockAtDpcLevel();
830 }
831
832 /* We are done */
833 KiReleaseDispatcherLock(Thread->WaitIrql);
834 return WaitStatus;
835
836 DontWait:
837 /* Release dispatcher lock but maintain high IRQL */
838 KiReleaseDispatcherLockFromDpcLevel();
839
840 /* Adjust the Quantum and return the wait status */
841 KiAdjustQuantumThread(Thread);
842 return WaitStatus;
843 }
844
845 NTSTATUS
846 NTAPI
847 NtDelayExecution(IN BOOLEAN Alertable,
848 IN PLARGE_INTEGER DelayInterval)
849 {
850 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
851 LARGE_INTEGER SafeInterval;
852 NTSTATUS Status;
853
854 /* Check the previous mode */
855 if (PreviousMode != KernelMode)
856 {
857 /* Enter SEH for probing */
858 _SEH2_TRY
859 {
860 /* Probe and capture the time out */
861 SafeInterval = ProbeForReadLargeInteger(DelayInterval);
862 DelayInterval = &SafeInterval;
863 }
864 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
865 {
866 /* Return the exception code */
867 _SEH2_YIELD(return _SEH2_GetExceptionCode());
868 }
869 _SEH2_END;
870 }
871
872 /* Call the Kernel Function */
873 Status = KeDelayExecutionThread(PreviousMode,
874 Alertable,
875 DelayInterval);
876
877 /* Return Status */
878 return Status;
879 }
880
881 /* EOF */