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