- Fix regression in Firefox installer by making KiWaitSatisfyAll a function instead...
[reactos.git] / reactos / ntoskrnl / ke / wait.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ke/wait.c
5 * PURPOSE: Manages waiting for Dispatcher Objects
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Gunnar Dalsnes
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS ******************************************************************/
18
19 KSPIN_LOCK DispatcherDatabaseLock;
20
21 /* PRIVATE FUNCTIONS *********************************************************/
22
23 /*
24 * Rules for checking alertability:
25 * - For Alertable waits ONLY:
26 * * We don't wait and return STATUS_ALERTED if the thread is alerted
27 * in EITHER the specified wait mode OR in Kernel Mode.
28 * - For BOTH Alertable AND Non-Alertable waits:
29 * * We don't want and return STATUS_USER_APC if the User Mode APC list
30 * is not empty AND the wait mode is User Mode.
31 */
32 #define KiCheckAlertability() \
33 if (Alertable) \
34 { \
35 if (CurrentThread->Alerted[(int)WaitMode]) \
36 { \
37 CurrentThread->Alerted[(int)WaitMode] = FALSE; \
38 WaitStatus = STATUS_ALERTED; \
39 break; \
40 } \
41 else if ((WaitMode != KernelMode) && \
42 (!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])))\
43 { \
44 CurrentThread->ApcState.UserApcPending = TRUE; \
45 WaitStatus = STATUS_USER_APC; \
46 break; \
47 } \
48 else if (CurrentThread->Alerted[KernelMode]) \
49 { \
50 CurrentThread->Alerted[KernelMode] = FALSE; \
51 WaitStatus = STATUS_ALERTED; \
52 break; \
53 } \
54 } \
55 else if ((WaitMode != KernelMode) && \
56 (CurrentThread->ApcState.UserApcPending)) \
57 { \
58 WaitStatus = STATUS_USER_APC; \
59 break; \
60 } \
61
62 /* PUBLIC FUNCTIONS **********************************************************/
63
64 VOID
65 FASTCALL
66 KiWaitSatisfyAll(PKWAIT_BLOCK FirstBlock)
67 {
68 PKWAIT_BLOCK WaitBlock = FirstBlock;
69 PKTHREAD WaitThread = WaitBlock->Thread;
70
71 /* Loop through all the Wait Blocks, and wake each Object */
72 do
73 {
74 /* Make sure it hasn't timed out */
75 if (WaitBlock->WaitKey != STATUS_TIMEOUT)
76 {
77 /* Wake the Object */
78 KiSatisfyObjectWait((PKMUTANT)WaitBlock->Object, WaitThread);
79 }
80
81 /* Move to the next block */
82 WaitBlock = WaitBlock->NextWaitBlock;
83 }
84 while (WaitBlock != FirstBlock);
85 }
86
87 /*
88 * @implemented
89 *
90 * FUNCTION: Puts the current thread into an alertable or nonalertable
91 * wait state for a given internal
92 * ARGUMENTS:
93 * WaitMode = Processor mode in which the caller is waiting
94 * Altertable = Specifies if the wait is alertable
95 * Interval = Specifies the interval to wait
96 * RETURNS: Status
97 */
98 NTSTATUS
99 STDCALL
100 KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
101 BOOLEAN Alertable,
102 PLARGE_INTEGER Interval)
103 {
104 PKWAIT_BLOCK TimerWaitBlock;
105 PKTIMER ThreadTimer;
106 PKTHREAD CurrentThread = KeGetCurrentThread();
107 NTSTATUS WaitStatus = STATUS_SUCCESS;
108 DPRINT("Entering KeDelayExecutionThread\n");
109
110 /* Check if the lock is already held */
111 if (CurrentThread->WaitNext)
112 {
113 /* Lock is held, disable Wait Next */
114 DPRINT("Lock is held\n");
115 CurrentThread->WaitNext = FALSE;
116 }
117 else
118 {
119 /* Lock not held, acquire it */
120 DPRINT("Lock is not held, acquiring\n");
121 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
122 }
123
124 /* Use built-in Wait block */
125 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
126
127 /* Start Wait Loop */
128 do
129 {
130 /* Check if a kernel APC is pending and we were below APC_LEVEL */
131 if ((CurrentThread->ApcState.KernelApcPending) &&
132 (CurrentThread->WaitIrql < APC_LEVEL))
133 {
134 /* Unlock the dispatcher */
135 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
136 goto SkipWait;
137 }
138
139 /* Check if we can do an alertable wait, if requested */
140 KiCheckAlertability();
141
142 /* Set status */
143 CurrentThread->WaitStatus = STATUS_WAIT_0;
144
145 /* Set Timer */
146 ThreadTimer = &CurrentThread->Timer;
147
148 /* Setup the Wait Block */
149 CurrentThread->WaitBlockList = TimerWaitBlock;
150 TimerWaitBlock->NextWaitBlock = TimerWaitBlock;
151
152 /* Link the timer to this Wait Block */
153 ThreadTimer->Header.WaitListHead.Flink = &TimerWaitBlock->WaitListEntry;
154 ThreadTimer->Header.WaitListHead.Blink = &TimerWaitBlock->WaitListEntry;
155
156 /* Insert the Timer into the Timer Lists and enable it */
157 if (!KiInsertTimer(ThreadTimer, *Interval))
158 {
159 /* FIXME: The timer already expired, we should find a new ready thread */
160 WaitStatus = STATUS_SUCCESS;
161 break;
162 }
163
164 /* Handle Kernel Queues */
165 if (CurrentThread->Queue)
166 {
167 DPRINT("Waking Queue\n");
168 KiWakeQueue(CurrentThread->Queue);
169 }
170
171 /* Setup the wait information */
172 CurrentThread->Alertable = Alertable;
173 CurrentThread->WaitMode = WaitMode;
174 CurrentThread->WaitReason = DelayExecution;
175 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
176 CurrentThread->State = Waiting;
177
178 /* Find a new thread to run */
179 DPRINT("Swapping threads\n");
180 WaitStatus = KiSwapThread();
181
182 /* Check if we were executing an APC or if we timed out */
183 if (WaitStatus != STATUS_KERNEL_APC)
184 {
185 /* This is a good thing */
186 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
187
188 /* Return Status */
189 return WaitStatus;
190 }
191
192 /* FIXME: Fixup interval */
193
194 /* Acquire again the lock */
195 SkipWait:
196 DPRINT("Looping again\n");
197 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
198 }
199 while (TRUE);
200
201 /* Release the Lock, we are done */
202 DPRINT("Returning from KeDelayExecutionThread(), %x. Status: %d\n",
203 KeGetCurrentThread(), Status);
204 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
205 return WaitStatus;
206 }
207
208 /*
209 * @implemented
210 *
211 * FUNCTION: Puts the current thread into a wait state until the
212 * given dispatcher object is set to signalled
213 * ARGUMENTS:
214 * Object = Object to wait on
215 * WaitReason = Reason for the wait (debugging aid)
216 * WaitMode = Can be KernelMode or UserMode, if UserMode then
217 * user-mode APCs can be delivered and the thread's
218 * stack can be paged out
219 * Altertable = Specifies if the wait is a alertable
220 * Timeout = Optional timeout value
221 * RETURNS: Status
222 */
223 NTSTATUS
224 STDCALL
225 KeWaitForSingleObject(PVOID Object,
226 KWAIT_REASON WaitReason,
227 KPROCESSOR_MODE WaitMode,
228 BOOLEAN Alertable,
229 PLARGE_INTEGER Timeout)
230 {
231 PKMUTANT CurrentObject;
232 PKWAIT_BLOCK WaitBlock;
233 PKWAIT_BLOCK TimerWaitBlock;
234 PKTIMER ThreadTimer;
235 PKTHREAD CurrentThread = KeGetCurrentThread();
236 NTSTATUS WaitStatus = STATUS_SUCCESS;
237 DPRINT("Entering KeWaitForSingleObject\n");
238
239 /* Check if the lock is already held */
240 if (CurrentThread->WaitNext)
241 {
242 /* Lock is held, disable Wait Next */
243 DPRINT("Lock is held\n");
244 CurrentThread->WaitNext = FALSE;
245 }
246 else
247 {
248 /* Lock not held, acquire it */
249 DPRINT("Lock is not held, acquiring\n");
250 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
251 }
252
253 /* Start the actual Loop */
254 do
255 {
256 /* Check if a kernel APC is pending and we were below APC_LEVEL */
257 if ((CurrentThread->ApcState.KernelApcPending) &&
258 (CurrentThread->WaitIrql < APC_LEVEL))
259 {
260 /* Unlock the dispatcher */
261 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
262 goto SkipWait;
263 }
264
265 /* Set default status */
266 CurrentThread->WaitStatus = STATUS_WAIT_0;
267
268 /* Append wait block to the KTHREAD wait block list */
269 CurrentThread->WaitBlockList = WaitBlock = &CurrentThread->WaitBlock[0];
270
271 /* Get the Current Object */
272 CurrentObject = (PKMUTANT)Object;
273
274 /* Check if it's a mutant */
275 if (CurrentObject->Header.Type == MutantObject)
276 {
277 /* Check its signal state or if we own it */
278 if ((CurrentObject->Header.SignalState > 0) ||
279 (CurrentThread == CurrentObject->OwnerThread))
280 {
281 /* Just unwait this guy and exit */
282 if (CurrentObject->Header.SignalState != (LONG)MINLONG)
283 {
284 /* It has a normal signal state, so unwait it and return */
285 KiSatisfyMutantWait(CurrentObject, CurrentThread);
286 WaitStatus = CurrentThread->WaitStatus;
287 goto DontWait;
288 }
289 else
290 {
291 /* According to wasm.ru, we must raise this exception (tested and true) */
292 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
293 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
294 }
295 }
296 }
297 else if (CurrentObject->Header.SignalState > 0)
298 {
299 /* Another satisfied object */
300 KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
301 WaitStatus = STATUS_WAIT_0;
302 goto DontWait;
303 }
304
305 /* Set up the Wait Block */
306 WaitBlock->Object = CurrentObject;
307 WaitBlock->Thread = CurrentThread;
308 WaitBlock->WaitKey = (USHORT)(STATUS_SUCCESS);
309 WaitBlock->WaitType = WaitAny;
310 WaitBlock->NextWaitBlock = WaitBlock;
311
312 /* Make sure we can satisfy the Alertable request */
313 KiCheckAlertability();
314
315 /* Enable the Timeout Timer if there was any specified */
316 if (Timeout)
317 {
318 /* Fail if the timeout interval is actually 0 */
319 if (!Timeout->QuadPart)
320 {
321 /* Return a timeout */
322 WaitStatus = STATUS_TIMEOUT;
323 goto DontWait;
324 }
325
326 /* Point to Timer Wait Block and Thread Timer */
327 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
328 ThreadTimer = &CurrentThread->Timer;
329
330 /* Connect the Timer Wait Block */
331 WaitBlock->NextWaitBlock = TimerWaitBlock;
332
333 /* Set up the Timer Wait Block */
334 TimerWaitBlock->NextWaitBlock = WaitBlock;
335
336 /* Link the timer to this Wait Block */
337 ThreadTimer->Header.WaitListHead.Flink = &TimerWaitBlock->WaitListEntry;
338 ThreadTimer->Header.WaitListHead.Blink = &TimerWaitBlock->WaitListEntry;
339
340 /* Insert the Timer into the Timer Lists and enable it */
341 if (!KiInsertTimer(ThreadTimer, *Timeout))
342 {
343 /* Return a timeout if we couldn't insert the timer */
344 WaitStatus = STATUS_TIMEOUT;
345 goto DontWait;
346 }
347 }
348
349 /* Link the Object to this Wait Block */
350 InsertTailList(&CurrentObject->Header.WaitListHead,
351 &WaitBlock->WaitListEntry);
352
353 /* Handle Kernel Queues */
354 if (CurrentThread->Queue)
355 {
356 DPRINT("Waking Queue\n");
357 KiWakeQueue(CurrentThread->Queue);
358 }
359
360 /* Setup the wait information */
361 CurrentThread->Alertable = Alertable;
362 CurrentThread->WaitMode = WaitMode;
363 CurrentThread->WaitReason = WaitReason;
364 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
365 CurrentThread->State = Waiting;
366
367 /* Find a new thread to run */
368 DPRINT("Swapping threads\n");
369 WaitStatus = KiSwapThread();
370
371 /* Check if we were executing an APC */
372 if (WaitStatus != STATUS_KERNEL_APC)
373 {
374 /* Return Status */
375 return WaitStatus;
376 }
377
378 /* Check if we had a timeout */
379 if (Timeout)
380 {
381 /* FIXME: Fixup interval */
382 }
383
384 /* Acquire again the lock */
385 SkipWait:
386 DPRINT("Looping again\n");
387 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
388 }
389 while (TRUE);
390
391 /* Release the Lock, we are done */
392 DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n",
393 KeGetCurrentThread(), WaitStatus);
394 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
395 return WaitStatus;
396
397 DontWait:
398 /* Adjust the Quantum */
399 KiAdjustQuantumThread(CurrentThread);
400
401 /* Release & Return */
402 DPRINT("Quick-return from KeWaitForMultipleObjects(), %x. Status: %d\n.",
403 KeGetCurrentThread(), WaitStatus);
404 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
405 return WaitStatus;
406 }
407
408 /*
409 * @implemented
410 */
411 NTSTATUS
412 STDCALL
413 KeWaitForMultipleObjects(ULONG Count,
414 PVOID Object[],
415 WAIT_TYPE WaitType,
416 KWAIT_REASON WaitReason,
417 KPROCESSOR_MODE WaitMode,
418 BOOLEAN Alertable,
419 PLARGE_INTEGER Timeout,
420 PKWAIT_BLOCK WaitBlockArray)
421 {
422 PKMUTANT CurrentObject;
423 PKWAIT_BLOCK WaitBlock;
424 PKWAIT_BLOCK TimerWaitBlock;
425 PKTIMER ThreadTimer;
426 PKTHREAD CurrentThread = KeGetCurrentThread();
427 ULONG AllObjectsSignaled;
428 ULONG WaitIndex;
429 NTSTATUS WaitStatus = STATUS_SUCCESS;
430 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
431 "PsGetCurrentThread() %x, Timeout %x\n",
432 Count, Object, PsGetCurrentThread(), Timeout);
433
434 /* Set the Current Thread */
435 CurrentThread = KeGetCurrentThread();
436
437 /* Check if the lock is already held */
438 if (CurrentThread->WaitNext)
439 {
440 /* Lock is held, disable Wait Next */
441 DPRINT("Lock is held\n");
442 CurrentThread->WaitNext = FALSE;
443 }
444 else
445 {
446 /* Lock not held, acquire it */
447 DPRINT("Lock is not held, acquiring\n");
448 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
449 }
450
451 /* Make sure the Wait Count is valid for the Thread and Maximum Wait Objects */
452 if (!WaitBlockArray)
453 {
454 /* Check in regards to the Thread Object Limit */
455 if (Count > THREAD_WAIT_OBJECTS) KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
456
457 /* Use the Thread's Wait Block */
458 WaitBlockArray = &CurrentThread->WaitBlock[0];
459 }
460 else
461 {
462 /* Using our own Block Array. Check in regards to System Object Limit */
463 if (Count > MAXIMUM_WAIT_OBJECTS) KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
464 }
465
466 /* Start the actual Loop */
467 do
468 {
469 /* Check if a kernel APC is pending and we were below APC_LEVEL */
470 if ((CurrentThread->ApcState.KernelApcPending) &&
471 (CurrentThread->WaitIrql < APC_LEVEL))
472 {
473 /* Unlock the dispatcher */
474 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
475 goto SkipWait;
476 }
477
478 /* Append wait block to the KTHREAD wait block list */
479 CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
480
481 /* Set default wait status */
482 CurrentThread->WaitStatus = STATUS_WAIT_0;
483
484 /* Check if the wait is (already) satisfied */
485 AllObjectsSignaled = TRUE;
486
487 /* First, we'll try to satisfy the wait directly */
488 for (WaitIndex = 0; WaitIndex < Count; WaitIndex++)
489 {
490 /* Get the Current Object */
491 CurrentObject = (PKMUTANT)Object[WaitIndex];
492
493 /* Check the type of wait */
494 if (WaitType == WaitAny)
495 {
496 /* Check if the Object is a mutant */
497 if (CurrentObject->Header.Type == MutantObject)
498 {
499 /* Check if it's signaled */
500 if ((CurrentObject->Header.SignalState > 0) ||
501 (CurrentThread == CurrentObject->OwnerThread))
502 {
503 /* This is a Wait Any, so just unwait this and exit */
504 if (CurrentObject->Header.SignalState != (LONG)MINLONG)
505 {
506 /* Normal signal state, so unwait it and return */
507 KiSatisfyMutantWait(CurrentObject, CurrentThread);
508 WaitStatus = CurrentThread->WaitStatus | WaitIndex;
509 goto DontWait;
510 }
511 else
512 {
513 /* According to wasm.ru, we must raise this exception (tested and true) */
514 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
515 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
516 }
517 }
518 }
519 else if (CurrentObject->Header.SignalState > 0)
520 {
521 /* Another signaled object, unwait and return */
522 KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
523 WaitStatus = WaitIndex;
524 goto DontWait;
525 }
526 }
527 else
528 {
529 /* Check if we're dealing with a mutant again */
530 if (CurrentObject->Header.Type == MutantObject)
531 {
532 /* Check if it has an invalid count */
533 if ((CurrentThread == CurrentObject->OwnerThread) &&
534 (CurrentObject->Header.SignalState == MINLONG))
535 {
536 /* Raise an exception */
537 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
538 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
539 }
540 else if ((CurrentObject->Header.SignalState <= 0) &&
541 (CurrentThread != CurrentObject->OwnerThread))
542 {
543 /* We don't own it, can't satisfy the wait */
544 AllObjectsSignaled = FALSE;
545 }
546 }
547 else if (CurrentObject->Header.SignalState <= 0)
548 {
549 /* Not signaled, can't satisfy */
550 AllObjectsSignaled = FALSE;
551 }
552 }
553
554 /* Set up a Wait Block for this Object */
555 WaitBlock->Object = CurrentObject;
556 WaitBlock->Thread = CurrentThread;
557 WaitBlock->WaitKey = (USHORT)WaitIndex;
558 WaitBlock->WaitType = (USHORT)WaitType;
559 WaitBlock->NextWaitBlock = WaitBlock + 1;
560
561 /* Move to the next Wait Block */
562 WaitBlock = WaitBlock->NextWaitBlock;
563 }
564
565 /* Return to the Root Wait Block */
566 WaitBlock--;
567 WaitBlock->NextWaitBlock = WaitBlockArray;
568
569 /* Check if this is a Wait All and all the objects are signaled */
570 if ((WaitType == WaitAll) && (AllObjectsSignaled))
571 {
572 /* Return to the Root Wait Block */
573 WaitBlock = CurrentThread->WaitBlockList;
574
575 /* Satisfy their Waits and return to the caller */
576 KiWaitSatisfyAll(WaitBlock);
577 WaitStatus = CurrentThread->WaitStatus;
578 goto DontWait;
579 }
580
581 /* Make sure we can satisfy the Alertable request */
582 KiCheckAlertability();
583
584 /* Enable the Timeout Timer if there was any specified */
585 if (Timeout)
586 {
587 /* Make sure the timeout interval isn't actually 0 */
588 if (!Timeout->QuadPart)
589 {
590 /* Return a timeout */
591 WaitStatus = STATUS_TIMEOUT;
592 goto DontWait;
593 }
594
595 /* Point to Timer Wait Block and Thread Timer */
596 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
597 ThreadTimer = &CurrentThread->Timer;
598
599 /* Connect the Timer Wait Block */
600 WaitBlock->NextWaitBlock = TimerWaitBlock;
601
602 /* Set up the Timer Wait Block */
603 TimerWaitBlock->NextWaitBlock = WaitBlockArray;
604
605 /* Initialize the list head */
606 InitializeListHead(&ThreadTimer->Header.WaitListHead);
607
608 /* Insert the Timer into the Timer Lists and enable it */
609 if (!KiInsertTimer(ThreadTimer, *Timeout))
610 {
611 /* Return a timeout if we couldn't insert the timer */
612 WaitStatus = STATUS_TIMEOUT;
613 goto DontWait;
614 }
615 }
616
617 /* Insert into Object's Wait List*/
618 WaitBlock = CurrentThread->WaitBlockList;
619 do
620 {
621 /* Get the Current Object */
622 CurrentObject = WaitBlock->Object;
623
624 /* Link the Object to this Wait Block */
625 InsertTailList(&CurrentObject->Header.WaitListHead,
626 &WaitBlock->WaitListEntry);
627
628 /* Move to the next Wait Block */
629 WaitBlock = WaitBlock->NextWaitBlock;
630 }
631 while (WaitBlock != WaitBlockArray);
632
633 /* Handle Kernel Queues */
634 if (CurrentThread->Queue)
635 {
636 DPRINT("Waking Queue\n");
637 KiWakeQueue(CurrentThread->Queue);
638 }
639
640 /* Setup the wait information */
641 CurrentThread->Alertable = Alertable;
642 CurrentThread->WaitMode = WaitMode;
643 CurrentThread->WaitReason = WaitReason;
644 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
645 CurrentThread->State = Waiting;
646
647 /* Find a new thread to run */
648 DPRINT("Swapping threads\n");
649 WaitStatus = KiSwapThread();
650
651 /* Check if we were executing an APC */
652 DPRINT("Thread is back\n");
653 if (WaitStatus != STATUS_KERNEL_APC)
654 {
655 /* Return Status */
656 return WaitStatus;
657 }
658
659 /* Check if we had a timeout */
660 if (Timeout)
661 {
662 /* FIXME: Fixup interval */
663 }
664
665 /* Acquire again the lock */
666 SkipWait:
667 DPRINT("Looping again\n");
668 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
669 }
670 while (TRUE);
671
672 /* Release the Lock, we are done */
673 DPRINT("Returning, %x. Status: %d\n", KeGetCurrentThread(), WaitStatus);
674 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
675 return WaitStatus;
676
677 DontWait:
678 /* Adjust the Quantum */
679 KiAdjustQuantumThread(CurrentThread);
680
681 /* Release & Return */
682 DPRINT("Returning, %x. Status: %d\n. We did not wait.",
683 KeGetCurrentThread(), WaitStatus);
684 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
685 return WaitStatus;
686 }
687
688 VOID
689 FASTCALL
690 KiWaitTest(PVOID ObjectPointer,
691 KPRIORITY Increment)
692 {
693 PLIST_ENTRY WaitEntry;
694 PLIST_ENTRY WaitList;
695 PKWAIT_BLOCK CurrentWaitBlock;
696 PKWAIT_BLOCK NextWaitBlock;
697 PKTHREAD WaitThread;
698 PKMUTANT FirstObject = ObjectPointer, Object;
699
700 /* Loop the Wait Entries */
701 DPRINT("KiWaitTest for Object: %x\n", FirstObject);
702 WaitList = &FirstObject->Header.WaitListHead;
703 WaitEntry = WaitList->Flink;
704 while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList))
705 {
706 /* Get the current wait block */
707 CurrentWaitBlock = CONTAINING_RECORD(WaitEntry,
708 KWAIT_BLOCK,
709 WaitListEntry);
710 WaitThread = CurrentWaitBlock->Thread;
711
712 /* Check the current Wait Mode */
713 if (CurrentWaitBlock->WaitType == WaitAny)
714 {
715 /* Easy case, satisfy only this wait */
716 DPRINT("Satisfiying a Wait any\n");
717 WaitEntry = WaitEntry->Blink;
718 KiSatisfyObjectWait(FirstObject, WaitThread);
719 }
720 else
721 {
722 /* Everything must be satisfied */
723 DPRINT("Checking for a Wait All\n");
724 NextWaitBlock = CurrentWaitBlock->NextWaitBlock;
725
726 /* Loop first to make sure they are valid */
727 while (NextWaitBlock != CurrentWaitBlock)
728 {
729 /* Check if the object is signaled */
730 Object = NextWaitBlock->Object;
731 DPRINT("Checking: %p %d\n",
732 Object, Object->Header.SignalState);
733 if (NextWaitBlock->WaitKey != STATUS_TIMEOUT)
734 {
735 /* Check if this is a mutant */
736 if ((Object->Header.Type == MutantObject) &&
737 (Object->Header.SignalState <= 0) &&
738 (WaitThread == Object->OwnerThread))
739 {
740 /* It's a signaled mutant */
741 }
742 else if (Object->Header.SignalState <= 0)
743 {
744 /* Skip the unwaiting */
745 goto SkipUnwait;
746 }
747 }
748
749 /* Go to the next Wait block */
750 NextWaitBlock = NextWaitBlock->NextWaitBlock;
751 }
752
753 /* All the objects are signaled, we can satisfy */
754 DPRINT("Satisfiying a Wait All\n");
755 WaitEntry = WaitEntry->Blink;
756 KiWaitSatisfyAll(CurrentWaitBlock);
757 }
758
759 /* All waits satisfied, unwait the thread */
760 DPRINT("Unwaiting the Thread\n");
761 KiAbortWaitThread(WaitThread, CurrentWaitBlock->WaitKey, Increment);
762
763 SkipUnwait:
764 /* Next entry */
765 WaitEntry = WaitEntry->Flink;
766 }
767
768 DPRINT("Done\n");
769 }
770
771 /* Must be called with the dispatcher lock held */
772 VOID
773 FASTCALL
774 KiAbortWaitThread(PKTHREAD Thread,
775 NTSTATUS WaitStatus,
776 KPRIORITY Increment)
777 {
778 PKWAIT_BLOCK WaitBlock;
779
780 /* If we are blocked, we must be waiting on something also */
781 DPRINT("KiAbortWaitThread: %x, Status: %x, %x \n",
782 Thread, WaitStatus, Thread->WaitBlockList);
783
784 /* Remove the Wait Blocks from the list */
785 DPRINT("Removing waits\n");
786 WaitBlock = Thread->WaitBlockList;
787 do
788 {
789 /* Remove it */
790 DPRINT("Removing Waitblock: %x, %x\n",
791 WaitBlock, WaitBlock->NextWaitBlock);
792 RemoveEntryList(&WaitBlock->WaitListEntry);
793
794 /* Go to the next one */
795 WaitBlock = WaitBlock->NextWaitBlock;
796 } while (WaitBlock != Thread->WaitBlockList);
797
798 /* Check if there's a Thread Timer */
799 if (Thread->Timer.Header.Inserted)
800 {
801 /* Cancel the Thread Timer with the no-lock fastpath */
802 DPRINT("Removing the Thread's Timer\n");
803 Thread->Timer.Header.Inserted = FALSE;
804 RemoveEntryList(&Thread->Timer.TimerListEntry);
805 }
806
807 /* Increment the Queue's active threads */
808 if (Thread->Queue)
809 {
810 DPRINT("Incrementing Queue's active threads\n");
811 Thread->Queue->CurrentCount++;
812 }
813
814 /* Reschedule the Thread */
815 DPRINT("Unblocking the Thread\n");
816 KiUnblockThread(Thread, &WaitStatus, 0);
817 }
818
819 VOID
820 FASTCALL
821 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
822 {
823 /* Increase contention count */
824 FastMutex->Contention++;
825
826 /* Wait for the event */
827 KeWaitForSingleObject(&FastMutex->Gate,
828 WrMutex,
829 KernelMode,
830 FALSE,
831 NULL);
832 }
833
834 VOID
835 FASTCALL
836 KiExitDispatcher(KIRQL OldIrql)
837 {
838 /* If it's the idle thread, dispatch */
839 if (!(KeIsExecutingDpc()) &&
840 (OldIrql < DISPATCH_LEVEL) &&
841 (KeGetCurrentThread()) &&
842 (KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread))
843 {
844 KiDispatchThreadNoLock(Ready);
845 }
846 else
847 {
848 KeReleaseDispatcherDatabaseLockFromDpcLevel();
849 }
850
851 /* Lower irql back */
852 KeLowerIrql(OldIrql);
853 }
854
855 /* EOF */