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