- Add the actual missing code to KiQuantumEnd. It's asserted to make sure it'll never...
[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 <internal/debug.h>
15
16 /* GLOBALS ******************************************************************/
17
18 KSPIN_LOCK DispatcherDatabaseLock;
19
20 /* PRIVATE FUNCTIONS *********************************************************/
21
22 VOID
23 FASTCALL
24 KiWaitSatisfyAll(PKWAIT_BLOCK FirstBlock)
25 {
26 PKWAIT_BLOCK WaitBlock = FirstBlock;
27 PKTHREAD WaitThread = WaitBlock->Thread;
28
29 /* Loop through all the Wait Blocks, and wake each Object */
30 do
31 {
32 /* Make sure it hasn't timed out */
33 if (WaitBlock->WaitKey != STATUS_TIMEOUT)
34 {
35 /* Wake the Object */
36 KiSatisfyObjectWait((PKMUTANT)WaitBlock->Object, WaitThread);
37 }
38
39 /* Move to the next block */
40 WaitBlock = WaitBlock->NextWaitBlock;
41 } while (WaitBlock != FirstBlock);
42 }
43
44 VOID
45 FASTCALL
46 KiWaitTest(IN PVOID ObjectPointer,
47 IN KPRIORITY Increment)
48 {
49 PLIST_ENTRY WaitEntry;
50 PLIST_ENTRY WaitList;
51 PKWAIT_BLOCK CurrentWaitBlock;
52 PKWAIT_BLOCK NextWaitBlock;
53 PKTHREAD WaitThread;
54 PKMUTANT FirstObject = ObjectPointer, Object;
55
56 /* Loop the Wait Entries */
57 WaitList = &FirstObject->Header.WaitListHead;
58 WaitEntry = WaitList->Flink;
59 while ((FirstObject->Header.SignalState > 0) &&
60 (WaitEntry != WaitList))
61 {
62 /* Get the current wait block */
63 CurrentWaitBlock = CONTAINING_RECORD(WaitEntry,
64 KWAIT_BLOCK,
65 WaitListEntry);
66 WaitThread = CurrentWaitBlock->Thread;
67
68 /* Check the current Wait Mode */
69 if (CurrentWaitBlock->WaitType == WaitAny)
70 {
71 /* Easy case, satisfy only this wait */
72 WaitEntry = WaitEntry->Blink;
73 KiSatisfyObjectWait(FirstObject, WaitThread);
74 }
75 else
76 {
77 /* Everything must be satisfied */
78 NextWaitBlock = CurrentWaitBlock->NextWaitBlock;
79
80 /* Loop first to make sure they are valid */
81 while (NextWaitBlock != CurrentWaitBlock)
82 {
83 /* Make sure this isn't a timeout block */
84 if (NextWaitBlock->WaitKey != STATUS_TIMEOUT)
85 {
86 /* Get the object */
87 Object = NextWaitBlock->Object;
88
89 /* Check if this is a mutant */
90 if ((Object->Header.Type == MutantObject) &&
91 (Object->Header.SignalState <= 0) &&
92 (WaitThread == Object->OwnerThread))
93 {
94 /* It's a signaled mutant */
95 }
96 else if (Object->Header.SignalState <= 0)
97 {
98 /* Skip the unwaiting */
99 goto SkipUnwait;
100 }
101 }
102
103 /* Go to the next Wait block */
104 NextWaitBlock = NextWaitBlock->NextWaitBlock;
105 }
106
107 /* All the objects are signaled, we can satisfy */
108 WaitEntry = WaitEntry->Blink;
109 KiWaitSatisfyAll(CurrentWaitBlock);
110 }
111
112 /* All waits satisfied, unwait the thread */
113 KiAbortWaitThread(WaitThread, CurrentWaitBlock->WaitKey, Increment);
114
115 SkipUnwait:
116 /* Next entry */
117 WaitEntry = WaitEntry->Flink;
118 }
119 }
120
121 /* Must be called with the dispatcher lock held */
122 VOID
123 FASTCALL
124 KiAbortWaitThread(IN PKTHREAD Thread,
125 IN NTSTATUS WaitStatus,
126 IN KPRIORITY Increment)
127 {
128 PKWAIT_BLOCK WaitBlock;
129 PKTIMER Timer;
130 LONG NewPriority;
131
132 /* Update wait status */
133 Thread->WaitStatus |= WaitStatus;
134
135 /* Remove the Wait Blocks from the list */
136 WaitBlock = Thread->WaitBlockList;
137 do
138 {
139 /* Remove it */
140 RemoveEntryList(&WaitBlock->WaitListEntry);
141
142 /* Go to the next one */
143 WaitBlock = WaitBlock->NextWaitBlock;
144 } while (WaitBlock != Thread->WaitBlockList);
145
146 /* Remove the thread from the wait list! */
147 if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry);
148
149 /* Check if there's a Thread Timer */
150 Timer = &Thread->Timer;
151 if (Timer->Header.Inserted)
152 {
153 /* Remove the timer */
154 Timer->Header.Inserted = FALSE;
155 RemoveEntryList(&Timer->TimerListEntry);
156 //KiRemoveTimer(Timer);
157 }
158
159 /* Increment the Queue's active threads */
160 if (Thread->Queue) Thread->Queue->CurrentCount++;
161
162 /* Check if this is a non-RT thread */
163 if (Thread->Priority < LOW_REALTIME_PRIORITY)
164 {
165 /* Check if boosting is enabled and we can boost */
166 if (!(Thread->DisableBoost) && !(Thread->PriorityDecrement))
167 {
168 /* We can boost, so calculate the new priority */
169 NewPriority = Thread->BasePriority + Increment;
170 if (NewPriority > Thread->Priority)
171 {
172 /* Make sure the new priority wouldn't push the thread to RT */
173 if (NewPriority >= LOW_REALTIME_PRIORITY)
174 {
175 /* Set it just before the RT zone */
176 Thread->Priority = LOW_REALTIME_PRIORITY - 1;
177 }
178 else
179 {
180 /* Otherwise, set our calculated priority */
181 Thread->Priority = NewPriority;
182 }
183 }
184 }
185
186 /* Check if this is a high-priority thread */
187 if (Thread->BasePriority >= 14)
188 {
189 /* It is, simply reset the quantum */
190 Thread->Quantum = Thread->QuantumReset;
191 }
192 else
193 {
194 /* Otherwise, decrease quantum */
195 Thread->Quantum--;
196 if (Thread->Quantum <= 0)
197 {
198 /* We've went below 0, reset it */
199 Thread->Quantum = Thread->QuantumReset;
200
201 /* Apply per-quantum priority decrement */
202 Thread->Priority -= (Thread->PriorityDecrement + 1);
203 if (Thread->Priority < Thread->BasePriority)
204 {
205 /* We've went too low, reset it */
206 Thread->Priority = Thread->BasePriority;
207 }
208
209 /* Delete per-quantum decrement */
210 Thread->PriorityDecrement = 0;
211 }
212 }
213 }
214 else
215 {
216 /* For real time threads, just reset the quantum */
217 Thread->Quantum = Thread->QuantumReset;
218 }
219
220 /* Reschedule the Thread */
221 KiReadyThread(Thread);
222 }
223
224 VOID
225 FASTCALL
226 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
227 {
228 /* Increase contention count */
229 FastMutex->Contention++;
230
231 /* Wait for the event */
232 KeWaitForSingleObject(&FastMutex->Gate,
233 WrMutex,
234 KernelMode,
235 FALSE,
236 NULL);
237 }
238
239 //
240 // This routine exits the dispatcher after a compatible operation and
241 // swaps the context to the next scheduled thread on the current CPU if
242 // one is available.
243 //
244 // It does NOT attempt to scan for a new thread to schedule.
245 //
246 VOID
247 FASTCALL
248 KiExitDispatcher(IN KIRQL OldIrql)
249 {
250 PKPRCB Prcb = KeGetCurrentPrcb();
251 PKTHREAD Thread, NextThread;
252 BOOLEAN PendingApc;
253
254 /* Make sure we're at synchronization level */
255 ASSERT_IRQL(SYNCH_LEVEL);
256
257 /* Check if we have deferred threads */
258 KiCheckDeferredReadyList(Prcb);
259
260 /* Check if we were called at dispatcher level or higher */
261 if (OldIrql >= DISPATCH_LEVEL)
262 {
263 /* Check if we have a thread to schedule, and that no DPC is active */
264 if ((Prcb->NextThread) && !(Prcb->DpcRoutineActive))
265 {
266 /* Request DPC interrupt */
267 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
268 }
269
270 /* Lower IRQL and exit */
271 goto Quickie;
272 }
273
274 /* Make sure there's a new thread scheduled */
275 if (!Prcb->NextThread) goto Quickie;
276
277 /* This shouldn't happen on ROS yet */
278 DPRINT1("The impossible happened - Tell Alex\n");
279 ASSERT(FALSE);
280
281 /* Lock the PRCB */
282 KiAcquirePrcbLock(Prcb);
283
284 /* Get the next and current threads now */
285 NextThread = Prcb->NextThread;
286 Thread = Prcb->CurrentThread;
287
288 /* Set current thread's swap busy to true */
289 KiSetThreadSwapBusy(Thread);
290
291 /* Switch threads in PRCB */
292 Prcb->NextThread = NULL;
293 Prcb->CurrentThread = NextThread;
294
295 /* Set thread to running */
296 NextThread->State = Running;
297
298 /* Queue it on the ready lists */
299 KxQueueReadyThread(Thread, Prcb);
300
301 /* Set wait IRQL */
302 Thread->WaitIrql = OldIrql;
303
304 /* Swap threads and check if APCs were pending */
305 PendingApc = KiSwapContext(Thread, NextThread);
306 if (PendingApc)
307 {
308 /* Lower only to APC */
309 KeLowerIrql(APC_LEVEL);
310
311 /* Deliver APCs */
312 KiDeliverApc(KernelMode, NULL, NULL);
313 ASSERT(OldIrql == PASSIVE_LEVEL);
314 }
315
316 /* Lower IRQl back */
317 Quickie:
318 KeLowerIrql(OldIrql);
319 }
320
321 /* PUBLIC FUNCTIONS **********************************************************/
322
323 /*
324 * @implemented
325 */
326 NTSTATUS
327 NTAPI
328 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
329 IN BOOLEAN Alertable,
330 IN PLARGE_INTEGER Interval OPTIONAL)
331 {
332 PKWAIT_BLOCK TimerWaitBlock;
333 PKTIMER ThreadTimer;
334 PKTHREAD CurrentThread = KeGetCurrentThread();
335 NTSTATUS WaitStatus = STATUS_SUCCESS;
336 BOOLEAN Swappable;
337 PLARGE_INTEGER OriginalDueTime = Interval;
338 LARGE_INTEGER DueTime, NewDueTime;
339
340 /* Check if the lock is already held */
341 if (CurrentThread->WaitNext)
342 {
343 /* Lock is held, disable Wait Next */
344 CurrentThread->WaitNext = FALSE;
345 }
346 else
347 {
348 /* Lock not held, acquire it */
349 CurrentThread->WaitIrql = KiAcquireDispatcherLock();
350 }
351
352 /* Use built-in Wait block */
353 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
354
355 /* Start Wait Loop */
356 do
357 {
358 /* Check if a kernel APC is pending and we're below APC_LEVEL */
359 if ((CurrentThread->ApcState.KernelApcPending) &&
360 !(CurrentThread->SpecialApcDisable) &&
361 (CurrentThread->WaitIrql < APC_LEVEL))
362 {
363 /* Unlock the dispatcher */
364 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
365 }
366 else
367 {
368 /* Check if we can do an alertable wait, if requested */
369 KiCheckAlertability();
370
371 /* Check if we can swap the thread's stack */
372 CurrentThread->WaitListEntry.Flink = NULL;
373 KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
374
375 /* Set status */
376 CurrentThread->WaitStatus = STATUS_WAIT_0;
377
378 /* Set Timer */
379 ThreadTimer = &CurrentThread->Timer;
380
381 /* Setup the Wait Block */
382 CurrentThread->WaitBlockList = TimerWaitBlock;
383 TimerWaitBlock->NextWaitBlock = TimerWaitBlock;
384
385 /* Link the timer to this Wait Block */
386 ThreadTimer->Header.WaitListHead.Flink =
387 &TimerWaitBlock->WaitListEntry;
388 ThreadTimer->Header.WaitListHead.Blink =
389 &TimerWaitBlock->WaitListEntry;
390
391 /* Insert the Timer into the Timer Lists and enable it */
392 if (!KiInsertTimer(ThreadTimer, *Interval))
393 {
394 /* FIXME: We should find a new ready thread */
395 WaitStatus = STATUS_SUCCESS;
396 break;
397 }
398
399 /* Save due time */
400 DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
401
402 /* Handle Kernel Queues */
403 if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
404
405 /* Setup the wait information */
406 CurrentThread->Alertable = Alertable;
407 CurrentThread->WaitMode = WaitMode;
408 CurrentThread->WaitReason = DelayExecution;
409 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
410 CurrentThread->State = Waiting;
411
412 /* Find a new thread to run */
413 KiAddThreadToWaitList(CurrentThread, Swappable);
414 WaitStatus = KiSwapThread(CurrentThread, KeGetCurrentPrcb());
415 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
416
417 /* Check if we were executing an APC or if we timed out */
418 if (WaitStatus != STATUS_KERNEL_APC)
419 {
420 /* This is a good thing */
421 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
422
423 /* Return Status */
424 return WaitStatus;
425 }
426
427 /* Recalculate due times */
428 Interval = KiRecalculateDueTime(OriginalDueTime,
429 &DueTime,
430 &NewDueTime);
431 }
432
433 /* Acquire again the lock */
434 CurrentThread->WaitIrql = KiAcquireDispatcherLock();
435 } while (TRUE);
436
437 /* Release the Lock, we are done */
438 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
439 return WaitStatus;
440 }
441
442 /*
443 * @implemented
444 */
445 NTSTATUS
446 NTAPI
447 KeWaitForSingleObject(IN PVOID Object,
448 IN KWAIT_REASON WaitReason,
449 IN KPROCESSOR_MODE WaitMode,
450 IN BOOLEAN Alertable,
451 IN PLARGE_INTEGER Timeout OPTIONAL)
452 {
453 PKMUTANT CurrentObject;
454 PKWAIT_BLOCK WaitBlock;
455 PKWAIT_BLOCK TimerWaitBlock;
456 PKTIMER ThreadTimer;
457 PKTHREAD CurrentThread = KeGetCurrentThread();
458 NTSTATUS WaitStatus = STATUS_SUCCESS;
459 BOOLEAN Swappable;
460 LARGE_INTEGER DueTime, NewDueTime;
461 PLARGE_INTEGER OriginalDueTime = Timeout;
462
463 /* Check if the lock is already held */
464 if (CurrentThread->WaitNext)
465 {
466 /* Lock is held, disable Wait Next */
467 CurrentThread->WaitNext = FALSE;
468 }
469 else
470 {
471 /* Lock not held, acquire it */
472 CurrentThread->WaitIrql = KiAcquireDispatcherLock();
473 }
474
475 /* Start the actual Loop */
476 WaitBlock = &CurrentThread->WaitBlock[0];
477 do
478 {
479 /* Check if a kernel APC is pending and we're below APC_LEVEL */
480 if ((CurrentThread->ApcState.KernelApcPending) &&
481 !(CurrentThread->SpecialApcDisable) &&
482 (CurrentThread->WaitIrql < APC_LEVEL))
483 {
484 /* Unlock the dispatcher */
485 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
486 }
487 else
488 {
489 /* Set default status */
490 CurrentThread->WaitStatus = STATUS_WAIT_0;
491
492 /* Get the Current Object */
493 CurrentObject = (PKMUTANT)Object;
494
495 /* Check if it's a mutant */
496 if (CurrentObject->Header.Type == MutantObject)
497 {
498 /* Check its signal state or if we own it */
499 if ((CurrentObject->Header.SignalState > 0) ||
500 (CurrentThread == CurrentObject->OwnerThread))
501 {
502 /* Just unwait this guy and exit */
503 if (CurrentObject->Header.SignalState != (LONG)MINLONG)
504 {
505 /* It has a normal signal state. Unwait and return */
506 KiSatisfyMutantWait(CurrentObject, CurrentThread);
507 WaitStatus = CurrentThread->WaitStatus;
508 goto DontWait;
509 }
510 else
511 {
512 /* Raise an exception (see wasm.ru) */
513 KiReleaseDispatcherLock(CurrentThread->
514 WaitIrql);
515 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
516 }
517 }
518 }
519 else if (CurrentObject->Header.SignalState > 0)
520 {
521 /* Another satisfied object */
522 KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
523 WaitStatus = STATUS_WAIT_0;
524 goto DontWait;
525 }
526
527 /* Append wait block to the KTHREAD wait block list */
528 CurrentThread->WaitBlockList = WaitBlock;
529
530 /* Set up the Wait Block */
531 WaitBlock->Object = CurrentObject;
532 WaitBlock->WaitKey = (USHORT)(STATUS_SUCCESS);
533 WaitBlock->WaitType = WaitAny;
534
535 /* Make sure we can satisfy the Alertable request */
536 KiCheckAlertability();
537
538 /* Check if we can swap the thread's stack */
539 CurrentThread->WaitListEntry.Flink = NULL;
540 KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
541
542 /* Enable the Timeout Timer if there was any specified */
543 if (Timeout)
544 {
545 /* Fail if the timeout interval is actually 0 */
546 if (!Timeout->QuadPart)
547 {
548 /* Return a timeout */
549 WaitStatus = STATUS_TIMEOUT;
550 goto DontWait;
551 }
552
553 /* Point to Timer Wait Block and Thread Timer */
554 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
555 ThreadTimer = &CurrentThread->Timer;
556
557 /* Connect the Timer Wait Block */
558 WaitBlock->NextWaitBlock = TimerWaitBlock;
559
560 /* Set up the Timer Wait Block */
561 TimerWaitBlock->NextWaitBlock = WaitBlock;
562
563 /* Link the timer to this Wait Block */
564 ThreadTimer->Header.WaitListHead.Flink =
565 &TimerWaitBlock->WaitListEntry;
566 ThreadTimer->Header.WaitListHead.Blink =
567 &TimerWaitBlock->WaitListEntry;
568
569 /* Insert the Timer into the Timer Lists and enable it */
570 if (!KiInsertTimer(ThreadTimer, *Timeout))
571 {
572 /* Return a timeout if we couldn't insert the timer */
573 WaitStatus = STATUS_TIMEOUT;
574 goto DontWait;
575 }
576
577 /* Set the current due time */
578 DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
579 }
580 else
581 {
582 /* No timer block, so just set our wait block as next */
583 WaitBlock->NextWaitBlock = WaitBlock;
584 }
585
586 /* Link the Object to this Wait Block */
587 InsertTailList(&CurrentObject->Header.WaitListHead,
588 &WaitBlock->WaitListEntry);
589
590 /* Handle Kernel Queues */
591 if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
592
593 /* Setup the wait information */
594 CurrentThread->Alertable = Alertable;
595 CurrentThread->WaitMode = WaitMode;
596 CurrentThread->WaitReason = WaitReason;
597 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
598 CurrentThread->State = Waiting;
599
600 /* Find a new thread to run */
601 KiAddThreadToWaitList(CurrentThread, Swappable);
602 WaitStatus = KiSwapThread(CurrentThread, KeGetCurrentPrcb());
603 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
604
605 /* Check if we were executing an APC */
606 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
607
608 /* Check if we had a timeout */
609 if (Timeout)
610 {
611 /* Recalculate due times */
612 Timeout = KiRecalculateDueTime(OriginalDueTime,
613 &DueTime,
614 &NewDueTime);
615 }
616 }
617
618 /* Acquire again the lock */
619 CurrentThread->WaitIrql = KiAcquireDispatcherLock();
620 } while (TRUE);
621
622 /* Release the Lock, we are done */
623 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
624 return WaitStatus;
625
626 DontWait:
627 /* Adjust the Quantum */
628 KiAdjustQuantumThread(CurrentThread);
629
630 /* Release & Return */
631 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
632 return WaitStatus;
633 }
634
635 /*
636 * @implemented
637 */
638 NTSTATUS
639 NTAPI
640 KeWaitForMultipleObjects(IN ULONG Count,
641 IN PVOID Object[],
642 IN WAIT_TYPE WaitType,
643 IN KWAIT_REASON WaitReason,
644 IN KPROCESSOR_MODE WaitMode,
645 IN BOOLEAN Alertable,
646 IN PLARGE_INTEGER Timeout OPTIONAL,
647 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
648 {
649 PKMUTANT CurrentObject;
650 PKWAIT_BLOCK WaitBlock;
651 PKWAIT_BLOCK TimerWaitBlock;
652 PKTIMER ThreadTimer;
653 PKTHREAD CurrentThread = KeGetCurrentThread();
654 ULONG AllObjectsSignaled;
655 ULONG WaitIndex;
656 NTSTATUS WaitStatus = STATUS_SUCCESS;
657 BOOLEAN Swappable;
658 PLARGE_INTEGER OriginalDueTime = Timeout;
659 LARGE_INTEGER DueTime, NewDueTime;
660
661 /* Set the Current Thread */
662 CurrentThread = KeGetCurrentThread();
663
664 /* Check if the lock is already held */
665 if (CurrentThread->WaitNext)
666 {
667 /* Lock is held, disable Wait Next */
668 CurrentThread->WaitNext = FALSE;
669 }
670 else
671 {
672 /* Lock not held, acquire it */
673 CurrentThread->WaitIrql = KiAcquireDispatcherLock();
674 }
675
676 /* Make sure the Wait Count is valid */
677 if (!WaitBlockArray)
678 {
679 /* Check in regards to the Thread Object Limit */
680 if (Count > THREAD_WAIT_OBJECTS)
681 {
682 /* Bugcheck */
683 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
684 }
685
686 /* Use the Thread's Wait Block */
687 WaitBlockArray = &CurrentThread->WaitBlock[0];
688 }
689 else
690 {
691 /* Using our own Block Array, so check with the System Object Limit */
692 if (Count > MAXIMUM_WAIT_OBJECTS)
693 {
694 /* Bugcheck */
695 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
696 }
697 }
698
699 /* Start the actual Loop */
700 do
701 {
702 /* Check if a kernel APC is pending and we're below APC_LEVEL */
703 if ((CurrentThread->ApcState.KernelApcPending) &&
704 !(CurrentThread->SpecialApcDisable) &&
705 (CurrentThread->WaitIrql < APC_LEVEL))
706 {
707 /* Unlock the dispatcher */
708 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
709 }
710 else
711 {
712 /* Append wait block to the KTHREAD wait block list */
713 CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
714
715 /* Set default wait status */
716 CurrentThread->WaitStatus = STATUS_WAIT_0;
717
718 /* Check if the wait is (already) satisfied */
719 AllObjectsSignaled = TRUE;
720
721 /* First, we'll try to satisfy the wait directly */
722 for (WaitIndex = 0; WaitIndex < Count; WaitIndex++)
723 {
724 /* Get the Current Object */
725 CurrentObject = (PKMUTANT)Object[WaitIndex];
726
727 /* Check the type of wait */
728 if (WaitType == WaitAny)
729 {
730 /* Check if the Object is a mutant */
731 if (CurrentObject->Header.Type == MutantObject)
732 {
733 /* Check if it's signaled */
734 if ((CurrentObject->Header.SignalState > 0) ||
735 (CurrentThread == CurrentObject->OwnerThread))
736 {
737 /* This is a Wait Any, so unwait this and exit */
738 if (CurrentObject->Header.SignalState !=
739 (LONG)MINLONG)
740 {
741 /* Normal signal state, unwait it and return */
742 KiSatisfyMutantWait(CurrentObject,
743 CurrentThread);
744 WaitStatus = CurrentThread->WaitStatus |
745 WaitIndex;
746 goto DontWait;
747 }
748 else
749 {
750 /* Raise an exception (see wasm.ru) */
751 KiReleaseDispatcherLock(CurrentThread->
752 WaitIrql);
753 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
754 }
755 }
756 }
757 else if (CurrentObject->Header.SignalState > 0)
758 {
759 /* Another signaled object, unwait and return */
760 KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
761 WaitStatus = WaitIndex;
762 goto DontWait;
763 }
764 }
765 else
766 {
767 /* Check if we're dealing with a mutant again */
768 if (CurrentObject->Header.Type == MutantObject)
769 {
770 /* Check if it has an invalid count */
771 if ((CurrentThread == CurrentObject->OwnerThread) &&
772 (CurrentObject->Header.SignalState == MINLONG))
773 {
774 /* Raise an exception */
775 KiReleaseDispatcherLock(CurrentThread->
776 WaitIrql);
777 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
778 }
779 else if ((CurrentObject->Header.SignalState <= 0) &&
780 (CurrentThread != CurrentObject->OwnerThread))
781 {
782 /* We don't own it, can't satisfy the wait */
783 AllObjectsSignaled = FALSE;
784 }
785 }
786 else if (CurrentObject->Header.SignalState <= 0)
787 {
788 /* Not signaled, can't satisfy */
789 AllObjectsSignaled = FALSE;
790 }
791 }
792
793 /* Set up a Wait Block for this Object */
794 WaitBlock->Object = CurrentObject;
795 WaitBlock->Thread = CurrentThread;
796 WaitBlock->WaitKey = (USHORT)WaitIndex;
797 WaitBlock->WaitType = (USHORT)WaitType;
798 WaitBlock->NextWaitBlock = WaitBlock + 1;
799
800 /* Move to the next Wait Block */
801 WaitBlock = WaitBlock->NextWaitBlock;
802 }
803
804 /* Return to the Root Wait Block */
805 WaitBlock--;
806 WaitBlock->NextWaitBlock = WaitBlockArray;
807
808 /* Check if this is a Wait All and all the objects are signaled */
809 if ((WaitType == WaitAll) && (AllObjectsSignaled))
810 {
811 /* Return to the Root Wait Block */
812 WaitBlock = CurrentThread->WaitBlockList;
813
814 /* Satisfy their Waits and return to the caller */
815 KiWaitSatisfyAll(WaitBlock);
816 WaitStatus = CurrentThread->WaitStatus;
817 goto DontWait;
818 }
819
820 /* Make sure we can satisfy the Alertable request */
821 KiCheckAlertability();
822
823 /* Check if we can swap the thread's stack */
824 CurrentThread->WaitListEntry.Flink = NULL;
825 KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
826
827 /* Enable the Timeout Timer if there was any specified */
828 if (Timeout)
829 {
830 /* Make sure the timeout interval isn't actually 0 */
831 if (!Timeout->QuadPart)
832 {
833 /* Return a timeout */
834 WaitStatus = STATUS_TIMEOUT;
835 goto DontWait;
836 }
837
838 /* Point to Timer Wait Block and Thread Timer */
839 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
840 ThreadTimer = &CurrentThread->Timer;
841
842 /* Connect the Timer Wait Block */
843 WaitBlock->NextWaitBlock = TimerWaitBlock;
844
845 /* Set up the Timer Wait Block */
846 TimerWaitBlock->NextWaitBlock = WaitBlockArray;
847
848 /* Initialize the list head */
849 InitializeListHead(&ThreadTimer->Header.WaitListHead);
850
851 /* Insert the Timer into the Timer Lists and enable it */
852 if (!KiInsertTimer(ThreadTimer, *Timeout))
853 {
854 /* Return a timeout if we couldn't insert the timer */
855 WaitStatus = STATUS_TIMEOUT;
856 goto DontWait;
857 }
858
859 /* Set the current due time */
860 DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
861 }
862
863 /* Insert into Object's Wait List*/
864 WaitBlock = CurrentThread->WaitBlockList;
865 do
866 {
867 /* Get the Current Object */
868 CurrentObject = WaitBlock->Object;
869
870 /* Link the Object to this Wait Block */
871 InsertTailList(&CurrentObject->Header.WaitListHead,
872 &WaitBlock->WaitListEntry);
873
874 /* Move to the next Wait Block */
875 WaitBlock = WaitBlock->NextWaitBlock;
876 } while (WaitBlock != WaitBlockArray);
877
878 /* Handle Kernel Queues */
879 if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
880
881 /* Setup the wait information */
882 CurrentThread->Alertable = Alertable;
883 CurrentThread->WaitMode = WaitMode;
884 CurrentThread->WaitReason = WaitReason;
885 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
886 CurrentThread->State = Waiting;
887
888 /* Find a new thread to run */
889 KiAddThreadToWaitList(CurrentThread, Swappable);
890 WaitStatus = KiSwapThread(CurrentThread, KeGetCurrentPrcb());
891 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
892
893 /* Check if we were executing an APC */
894 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
895
896 /* Check if we had a timeout */
897 if (Timeout)
898 {
899 /* Recalculate due times */
900 Timeout = KiRecalculateDueTime(OriginalDueTime,
901 &DueTime,
902 &NewDueTime);
903 }
904
905 /* Acquire again the lock */
906 CurrentThread->WaitIrql = KiAcquireDispatcherLock();
907 }
908 } while (TRUE);
909
910 /* Release the Lock, we are done */
911 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
912 return WaitStatus;
913
914 DontWait:
915 /* Adjust the Quantum */
916 KiAdjustQuantumThread(CurrentThread);
917
918 /* Release & Return */
919 KiReleaseDispatcherLock(CurrentThread->WaitIrql);
920 return WaitStatus;
921 }
922
923 NTSTATUS
924 NTAPI
925 NtDelayExecution(IN BOOLEAN Alertable,
926 IN PLARGE_INTEGER DelayInterval)
927 {
928 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
929 LARGE_INTEGER SafeInterval;
930 NTSTATUS Status = STATUS_SUCCESS;
931
932 /* Check if parameters are valid */
933 if(PreviousMode != KernelMode)
934 {
935 _SEH_TRY
936 {
937 /* make a copy on the kernel stack and let DelayInterval point to it so
938 we don't need to wrap KeDelayExecutionThread in SEH! */
939 SafeInterval = ProbeForReadLargeInteger(DelayInterval);
940 DelayInterval = &SafeInterval;
941 }
942 _SEH_HANDLE
943 {
944 Status = _SEH_GetExceptionCode();
945 }
946 _SEH_END;
947
948 if (!NT_SUCCESS(Status)) return Status;
949 }
950
951 /* Call the Kernel Function */
952 Status = KeDelayExecutionThread(PreviousMode,
953 Alertable,
954 DelayInterval);
955
956 /* Return Status */
957 return Status;
958 }
959
960 /* EOF */