Sync with trunk r58151 to bring the latest changes from Amine and Timo.
[reactos.git] / 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 LONG_PTR 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->Event,
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(OldIrql, Thread);
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 = (NTSTATUS)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 = {{0}}, NewDueTime, InterruptTime;
417 PLARGE_INTEGER OriginalDueTime = Timeout;
418 ULONG Hand = 0;
419
420 if (Thread->WaitNext)
421 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
422 else
423 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL ||
424 (KeGetCurrentIrql() == DISPATCH_LEVEL &&
425 Timeout && Timeout->QuadPart == 0));
426
427 /* Check if the lock is already held */
428 if (!Thread->WaitNext) goto WaitStart;
429
430 /* Otherwise, we already have the lock, so initialize the wait */
431 Thread->WaitNext = FALSE;
432 KxSingleThreadWait();
433
434 /* Start wait loop */
435 for (;;)
436 {
437 /* Disable pre-emption */
438 Thread->Preempted = FALSE;
439
440 /* Check if a kernel APC is pending and we're below APC_LEVEL */
441 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
442 (Thread->WaitIrql < APC_LEVEL))
443 {
444 /* Unlock the dispatcher */
445 KiReleaseDispatcherLock(Thread->WaitIrql);
446 }
447 else
448 {
449 /* Sanity check */
450 ASSERT(CurrentObject->Header.Type != QueueObject);
451
452 /* Check if it's a mutant */
453 if (CurrentObject->Header.Type == MutantObject)
454 {
455 /* Check its signal state or if we own it */
456 if ((CurrentObject->Header.SignalState > 0) ||
457 (Thread == CurrentObject->OwnerThread))
458 {
459 /* Just unwait this guy and exit */
460 if (CurrentObject->Header.SignalState != (LONG)MINLONG)
461 {
462 /* It has a normal signal state. Unwait and return */
463 KiSatisfyMutantWait(CurrentObject, Thread);
464 WaitStatus = (NTSTATUS)Thread->WaitStatus;
465 goto DontWait;
466 }
467 else
468 {
469 /* Raise an exception */
470 KiReleaseDispatcherLock(Thread->WaitIrql);
471 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
472 }
473 }
474 }
475 else if (CurrentObject->Header.SignalState > 0)
476 {
477 /* Another satisfied object */
478 KiSatisfyNonMutantWait(CurrentObject);
479 WaitStatus = STATUS_WAIT_0;
480 goto DontWait;
481 }
482
483 /* Make sure we can satisfy the Alertable request */
484 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
485 if (WaitStatus != STATUS_WAIT_0) break;
486
487 /* Enable the Timeout Timer if there was any specified */
488 if (Timeout)
489 {
490 /* Check if the timer expired */
491 InterruptTime.QuadPart = KeQueryInterruptTime();
492 if ((ULONGLONG)InterruptTime.QuadPart >=
493 Timer->DueTime.QuadPart)
494 {
495 /* It did, so we don't need to wait */
496 WaitStatus = STATUS_TIMEOUT;
497 goto DontWait;
498 }
499
500 /* It didn't, so activate it */
501 Timer->Header.Inserted = TRUE;
502 }
503
504 /* Link the Object to this Wait Block */
505 InsertTailList(&CurrentObject->Header.WaitListHead,
506 &WaitBlock->WaitListEntry);
507
508 /* Handle Kernel Queues */
509 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
510
511 /* Setup the wait information */
512 Thread->State = Waiting;
513
514 /* Add the thread to the wait list */
515 KiAddThreadToWaitList(Thread, Swappable);
516
517 /* Activate thread swap */
518 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
519 KiSetThreadSwapBusy(Thread);
520
521 /* Check if we have a timer */
522 if (Timeout)
523 {
524 /* Insert it */
525 KxInsertTimer(Timer, Hand);
526 }
527 else
528 {
529 /* Otherwise, unlock the dispatcher */
530 KiReleaseDispatcherLockFromDpcLevel();
531 }
532
533 /* Do the actual swap */
534 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
535
536 /* Check if we were executing an APC */
537 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
538
539 /* Check if we had a timeout */
540 if (Timeout)
541 {
542 /* Recalculate due times */
543 Timeout = KiRecalculateDueTime(OriginalDueTime,
544 &DueTime,
545 &NewDueTime);
546 }
547 }
548 WaitStart:
549 /* Setup a new wait */
550 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
551 KxSingleThreadWait();
552 KiAcquireDispatcherLockAtDpcLevel();
553 }
554
555 /* Wait complete */
556 KiReleaseDispatcherLock(Thread->WaitIrql);
557 return WaitStatus;
558
559 DontWait:
560 /* Release dispatcher lock but maintain high IRQL */
561 KiReleaseDispatcherLockFromDpcLevel();
562
563 /* Adjust the Quantum and return the wait status */
564 KiAdjustQuantumThread(Thread);
565 return WaitStatus;
566 }
567
568 /*
569 * @implemented
570 */
571 NTSTATUS
572 NTAPI
573 KeWaitForMultipleObjects(IN ULONG Count,
574 IN PVOID Object[],
575 IN WAIT_TYPE WaitType,
576 IN KWAIT_REASON WaitReason,
577 IN KPROCESSOR_MODE WaitMode,
578 IN BOOLEAN Alertable,
579 IN PLARGE_INTEGER Timeout OPTIONAL,
580 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
581 {
582 PKMUTANT CurrentObject;
583 PKWAIT_BLOCK WaitBlock;
584 PKTHREAD Thread = KeGetCurrentThread();
585 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
586 PKTIMER Timer = &Thread->Timer;
587 NTSTATUS WaitStatus = STATUS_SUCCESS;
588 BOOLEAN Swappable;
589 PLARGE_INTEGER OriginalDueTime = Timeout;
590 LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime;
591 ULONG Index, Hand = 0;
592
593 if (Thread->WaitNext)
594 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
595 else if (KeGetCurrentIrql() == DISPATCH_LEVEL &&
596 (!Timeout || Timeout->QuadPart != 0))
597 {
598 /* HACK: tcpip is broken and waits with spinlocks acquired (bug #7129) */
599 DPRINT("%s called at DISPATCH_LEVEL with non-zero timeout!\n",
600 __FUNCTION__);
601 }
602 else
603 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
604
605 /* Make sure the Wait Count is valid */
606 if (!WaitBlockArray)
607 {
608 /* Check in regards to the Thread Object Limit */
609 if (Count > THREAD_WAIT_OBJECTS)
610 {
611 /* Bugcheck */
612 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
613 }
614
615 /* Use the Thread's Wait Block */
616 WaitBlockArray = &Thread->WaitBlock[0];
617 }
618 else
619 {
620 /* Using our own Block Array, so check with the System Object Limit */
621 if (Count > MAXIMUM_WAIT_OBJECTS)
622 {
623 /* Bugcheck */
624 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
625 }
626 }
627
628 /* Sanity check */
629 ASSERT(Count != 0);
630
631 /* Check if the lock is already held */
632 if (!Thread->WaitNext) goto WaitStart;
633
634 /* Otherwise, we already have the lock, so initialize the wait */
635 Thread->WaitNext = FALSE;
636 /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */
637 /* uses (and modifies some of) the following local */
638 /* variables: */
639 /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */
640 /* If it looks like this code doesn't actually wait for any objects */
641 /* at all, it's because the setup is done by that macro. */
642 KxMultiThreadWait();
643
644 /* Start wait loop */
645 for (;;)
646 {
647 /* Disable pre-emption */
648 Thread->Preempted = FALSE;
649
650 /* Check if a kernel APC is pending and we're below APC_LEVEL */
651 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
652 (Thread->WaitIrql < APC_LEVEL))
653 {
654 /* Unlock the dispatcher */
655 KiReleaseDispatcherLock(Thread->WaitIrql);
656 }
657 else
658 {
659 /* Check what kind of wait this is */
660 Index = 0;
661 if (WaitType == WaitAny)
662 {
663 /* Loop blocks */
664 do
665 {
666 /* Get the Current Object */
667 CurrentObject = (PKMUTANT)Object[Index];
668 ASSERT(CurrentObject->Header.Type != QueueObject);
669
670 /* Check if the Object is a mutant */
671 if (CurrentObject->Header.Type == MutantObject)
672 {
673 /* Check if it's signaled */
674 if ((CurrentObject->Header.SignalState > 0) ||
675 (Thread == CurrentObject->OwnerThread))
676 {
677 /* This is a Wait Any, so unwait this and exit */
678 if (CurrentObject->Header.SignalState !=
679 (LONG)MINLONG)
680 {
681 /* Normal signal state, unwait it and return */
682 KiSatisfyMutantWait(CurrentObject, Thread);
683 WaitStatus = (NTSTATUS)Thread->WaitStatus | Index;
684 goto DontWait;
685 }
686 else
687 {
688 /* Raise an exception (see wasm.ru) */
689 KiReleaseDispatcherLock(Thread->WaitIrql);
690 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
691 }
692 }
693 }
694 else if (CurrentObject->Header.SignalState > 0)
695 {
696 /* Another signaled object, unwait and return */
697 KiSatisfyNonMutantWait(CurrentObject);
698 WaitStatus = Index;
699 goto DontWait;
700 }
701
702 /* Go to the next block */
703 Index++;
704 } while (Index < Count);
705 }
706 else
707 {
708 /* Loop blocks */
709 do
710 {
711 /* Get the Current Object */
712 CurrentObject = (PKMUTANT)Object[Index];
713 ASSERT(CurrentObject->Header.Type != QueueObject);
714
715 /* Check if we're dealing with a mutant again */
716 if (CurrentObject->Header.Type == MutantObject)
717 {
718 /* Check if it has an invalid count */
719 if ((Thread == CurrentObject->OwnerThread) &&
720 (CurrentObject->Header.SignalState == (LONG)MINLONG))
721 {
722 /* Raise an exception */
723 KiReleaseDispatcherLock(Thread->WaitIrql);
724 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
725 }
726 else if ((CurrentObject->Header.SignalState <= 0) &&
727 (Thread != CurrentObject->OwnerThread))
728 {
729 /* We don't own it, can't satisfy the wait */
730 break;
731 }
732 }
733 else if (CurrentObject->Header.SignalState <= 0)
734 {
735 /* Not signaled, can't satisfy */
736 break;
737 }
738
739 /* Go to the next block */
740 Index++;
741 } while (Index < Count);
742
743 /* Check if we've went through all the objects */
744 if (Index == Count)
745 {
746 /* Loop wait blocks */
747 WaitBlock = WaitBlockArray;
748 do
749 {
750 /* Get the object and satisfy it */
751 CurrentObject = (PKMUTANT)WaitBlock->Object;
752 KiSatisfyObjectWait(CurrentObject, Thread);
753
754 /* Go to the next block */
755 WaitBlock = WaitBlock->NextWaitBlock;
756 } while(WaitBlock != WaitBlockArray);
757
758 /* Set the wait status and get out */
759 WaitStatus = (NTSTATUS)Thread->WaitStatus;
760 goto DontWait;
761 }
762 }
763
764 /* Make sure we can satisfy the Alertable request */
765 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
766 if (WaitStatus != STATUS_WAIT_0) break;
767
768 /* Enable the Timeout Timer if there was any specified */
769 if (Timeout)
770 {
771 /* Check if the timer expired */
772 InterruptTime.QuadPart = KeQueryInterruptTime();
773 if ((ULONGLONG)InterruptTime.QuadPart >=
774 Timer->DueTime.QuadPart)
775 {
776 /* It did, so we don't need to wait */
777 WaitStatus = STATUS_TIMEOUT;
778 goto DontWait;
779 }
780
781 /* It didn't, so activate it */
782 Timer->Header.Inserted = TRUE;
783
784 /* Link the wait blocks */
785 WaitBlock->NextWaitBlock = TimerBlock;
786 }
787
788 /* Insert into Object's Wait List*/
789 WaitBlock = WaitBlockArray;
790 do
791 {
792 /* Get the Current Object */
793 CurrentObject = WaitBlock->Object;
794
795 /* Link the Object to this Wait Block */
796 InsertTailList(&CurrentObject->Header.WaitListHead,
797 &WaitBlock->WaitListEntry);
798
799 /* Move to the next Wait Block */
800 WaitBlock = WaitBlock->NextWaitBlock;
801 } while (WaitBlock != WaitBlockArray);
802
803 /* Handle Kernel Queues */
804 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
805
806 /* Setup the wait information */
807 Thread->State = Waiting;
808
809 /* Add the thread to the wait list */
810 KiAddThreadToWaitList(Thread, Swappable);
811
812 /* Activate thread swap */
813 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
814 KiSetThreadSwapBusy(Thread);
815
816 /* Check if we have a timer */
817 if (Timeout)
818 {
819 /* Insert it */
820 KxInsertTimer(Timer, Hand);
821 }
822 else
823 {
824 /* Otherwise, unlock the dispatcher */
825 KiReleaseDispatcherLockFromDpcLevel();
826 }
827
828 /* Swap the thread */
829 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
830
831 /* Check if we were executing an APC */
832 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
833
834 /* Check if we had a timeout */
835 if (Timeout)
836 {
837 /* Recalculate due times */
838 Timeout = KiRecalculateDueTime(OriginalDueTime,
839 &DueTime,
840 &NewDueTime);
841 }
842 }
843
844 WaitStart:
845 /* Setup a new wait */
846 Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
847 KxMultiThreadWait();
848 KiAcquireDispatcherLockAtDpcLevel();
849 }
850
851 /* We are done */
852 KiReleaseDispatcherLock(Thread->WaitIrql);
853 return WaitStatus;
854
855 DontWait:
856 /* Release dispatcher lock but maintain high IRQL */
857 KiReleaseDispatcherLockFromDpcLevel();
858
859 /* Adjust the Quantum and return the wait status */
860 KiAdjustQuantumThread(Thread);
861 return WaitStatus;
862 }
863
864 NTSTATUS
865 NTAPI
866 NtDelayExecution(IN BOOLEAN Alertable,
867 IN PLARGE_INTEGER DelayInterval)
868 {
869 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
870 LARGE_INTEGER SafeInterval;
871 NTSTATUS Status;
872
873 /* Check the previous mode */
874 if (PreviousMode != KernelMode)
875 {
876 /* Enter SEH for probing */
877 _SEH2_TRY
878 {
879 /* Probe and capture the time out */
880 SafeInterval = ProbeForReadLargeInteger(DelayInterval);
881 DelayInterval = &SafeInterval;
882 }
883 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
884 {
885 /* Return the exception code */
886 _SEH2_YIELD(return _SEH2_GetExceptionCode());
887 }
888 _SEH2_END;
889 }
890
891 /* Call the Kernel Function */
892 Status = KeDelayExecutionThread(PreviousMode,
893 Alertable,
894 DelayInterval);
895
896 /* Return Status */
897 return Status;
898 }
899
900 /* EOF */