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