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