Fix WaitAll bug. Previously, not all Wait Objects in a WaitAll were checked for signa...
[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 == UserMode)) {
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 == UserMode)) {
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 /* FIXME:
229 * Temporary hack until my Object Manager re-write. Basically some objects, like
230 * the File Object, but also LPCs and others, are actually waitable on their event.
231 * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member,
232 * by using pretty much the same kind of hack as us. Normal objects point to themselves
233 * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by
234 * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however,
235 * I will keep this hack here, since there's no need to make an interim hack until the rewrite
236 * -- Alex Ionescu 24/02/05
237 */
238 if (CurrentObject->Type == IO_TYPE_FILE) {
239
240 DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event);
241 CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event);
242 }
243
244 /* Check if the Object is Signaled */
245 if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
246
247 /* Just unwait this guy and exit */
248 if (CurrentObject->SignalState != MINLONG) {
249
250 /* It has a normal signal state, so unwait it and return */
251 KiSatisfyObjectWait(CurrentObject, CurrentThread);
252 Status = STATUS_WAIT_0;
253 goto WaitDone;
254
255 } else {
256
257 /* Is this a Mutant? */
258 if (CurrentObject->Type == MutantObject) {
259
260 /* According to wasm.ru, we must raise this exception (tested and true) */
261 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
262 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
263 }
264 }
265 }
266
267 /* Set up the Wait Block */
268 WaitBlock->Object = CurrentObject;
269 WaitBlock->Thread = CurrentThread;
270 WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0);
271 WaitBlock->WaitType = WaitAny;
272 WaitBlock->NextWaitBlock = WaitBlock;
273
274 /* Make sure we can satisfy the Alertable request */
275 KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status);
276
277 /* Set the Wait Status */
278 CurrentThread->WaitStatus = Status;
279
280 /* Enable the Timeout Timer if there was any specified */
281 if (Timeout != NULL) {
282
283 /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
284 if (!Timeout->QuadPart) {
285
286 /* Return a timeout */
287 Status = STATUS_TIMEOUT;
288 goto WaitDone;
289 }
290
291 /* Point to Timer Wait Block and Thread Timer */
292 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
293 ThreadTimer = &CurrentThread->Timer;
294
295 /* Connect the Timer Wait Block */
296 WaitBlock->NextWaitBlock = TimerWaitBlock;
297
298 /* Set up the Timer Wait Block */
299 TimerWaitBlock->Object = (PVOID)ThreadTimer;
300 TimerWaitBlock->Thread = CurrentThread;
301 TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
302 TimerWaitBlock->WaitType = WaitAny;
303 TimerWaitBlock->NextWaitBlock = WaitBlock;
304
305 /* Link the timer to this Wait Block */
306 InitializeListHead(&ThreadTimer->Header.WaitListHead);
307 InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
308
309 /* Insert the Timer into the Timer Lists and enable it */
310 if (!KiInsertTimer(ThreadTimer, *Timeout)) {
311
312 /* Return a timeout if we couldn't insert the timer for some reason */
313 Status = STATUS_TIMEOUT;
314 goto WaitDone;
315 }
316 }
317
318 /* Link the Object to this Wait Block */
319 InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
320
321 /* Handle Kernel Queues */
322 if (CurrentThread->Queue) {
323
324 DPRINT("Waking Queue\n");
325 KiWakeQueue(CurrentThread->Queue);
326 }
327
328 /* Block the Thread */
329 DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread());
330 KiBlockThread(&Status,
331 Alertable,
332 WaitMode,
333 (UCHAR)WaitReason);
334
335 /* Check if we were executing an APC */
336 if (Status != STATUS_KERNEL_APC) {
337
338 /* Return Status */
339 return Status;
340 }
341
342 DPRINT("Looping Again\n");
343 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
344
345 } while (TRUE);
346
347 WaitDone:
348 /* Release the Lock, we are done */
349 DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status);
350 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
351 return Status;
352 }
353
354 /*
355 * @implemented
356 */
357 NTSTATUS STDCALL
358 KeWaitForMultipleObjects(ULONG Count,
359 PVOID Object[],
360 WAIT_TYPE WaitType,
361 KWAIT_REASON WaitReason,
362 KPROCESSOR_MODE WaitMode,
363 BOOLEAN Alertable,
364 PLARGE_INTEGER Timeout,
365 PKWAIT_BLOCK WaitBlockArray)
366 {
367 PDISPATCHER_HEADER CurrentObject;
368 PKWAIT_BLOCK WaitBlock;
369 PKWAIT_BLOCK TimerWaitBlock;
370 PKTIMER ThreadTimer;
371 PKTHREAD CurrentThread = KeGetCurrentThread();
372 ULONG AllObjectsSignaled;
373 ULONG WaitIndex;
374 NTSTATUS Status;
375 NTSTATUS WaitStatus;
376
377 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
378 "PsGetCurrentThread() %x, Timeout %x\n", Count, Object, PsGetCurrentThread(), Timeout);
379
380 /* Set the Current Thread */
381 CurrentThread = KeGetCurrentThread();
382
383 /* Check if the lock is already held */
384 if (CurrentThread->WaitNext) {
385
386 /* Lock is held, disable Wait Next */
387 DPRINT("Lock is held\n");
388 CurrentThread->WaitNext = FALSE;
389
390 } else {
391
392 /* Lock not held, acquire it */
393 DPRINT("Lock is not held, acquiring\n");
394 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
395 }
396
397 /* Make sure the Wait Count is valid for the Thread and Maximum Wait Objects */
398 if (!WaitBlockArray) {
399
400 /* Check in regards to the Thread Object Limit */
401 if (Count > THREAD_WAIT_OBJECTS) {
402
403 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
404 }
405
406 /* Use the Thread's Wait Block */
407 WaitBlockArray = &CurrentThread->WaitBlock[0];
408
409 } else {
410
411 /* Using our own Block Array. Check in regards to System Object Limit */
412 if (Count > MAXIMUM_WAIT_OBJECTS) {
413
414 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
415 }
416 }
417
418 /* Start the actual Loop */
419 do {
420
421 /* Get the current Wait Status */
422 WaitStatus = CurrentThread->WaitStatus;
423
424 /* Append wait block to the KTHREAD wait block list */
425 CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
426
427 /* Check if the wait is (already) satisfied */
428 AllObjectsSignaled = TRUE;
429
430 /* First, we'll try to satisfy the wait directly */
431 for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) {
432
433 /* Get the Current Object */
434 CurrentObject = (PDISPATCHER_HEADER)Object[WaitIndex];
435
436 /* FIXME:
437 * Temporary hack until my Object Manager re-write. Basically some objects, like
438 * the File Object, but also LPCs and others, are actually waitable on their event.
439 * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member,
440 * by using pretty much the same kind of hack as us. Normal objects point to themselves
441 * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by
442 * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however,
443 * I will keep this hack here, since there's no need to make an interim hack until the rewrite
444 * -- Alex Ionescu 24/02/05
445 */
446 if (CurrentObject->Type == IO_TYPE_FILE) {
447
448 DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event);
449 CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event);
450 }
451
452 /* Check if the Object is Signaled */
453 if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
454
455 /* Check what kind of wait this is */
456 if (WaitType == WaitAny) {
457
458 /* This is a Wait Any, so just unwait this guy and exit */
459 if (CurrentObject->SignalState != MINLONG) {
460
461 /* It has a normal signal state, so unwait it and return */
462 KiSatisfyObjectWait(CurrentObject, CurrentThread);
463 Status = STATUS_WAIT_0 | WaitIndex;
464 goto WaitDone;
465
466 } else {
467
468 /* Is this a Mutant? */
469 if (CurrentObject->Type == MutantObject) {
470
471 /* According to wasm.ru, we must raise this exception (tested and true) */
472 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
473 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
474 }
475 }
476 }
477
478 } else {
479
480 /* One of the objects isn't signaled... if this is a WaitAll, we will fail later */
481 AllObjectsSignaled = FALSE;
482 }
483
484 /* Set up a Wait Block for this Object */
485 WaitBlock->Object = CurrentObject;
486 WaitBlock->Thread = CurrentThread;
487 WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0 + WaitIndex);
488 WaitBlock->WaitType = (USHORT)WaitType;
489 WaitBlock->NextWaitBlock = WaitBlock + 1;
490
491 /* Move to the next Wait Block */
492 WaitBlock = WaitBlock->NextWaitBlock;
493 }
494
495 /* Return to the Root Wait Block */
496 WaitBlock--;
497 WaitBlock->NextWaitBlock = WaitBlockArray;
498
499 /* Check if this is a Wait All and all the objects are signaled */
500 if ((WaitType == WaitAll) && (AllObjectsSignaled)) {
501
502 /* Return to the Root Wait Block */
503 WaitBlock = CurrentThread->WaitBlockList;
504
505 /* Satisfy their Waits and return to the caller */
506 KiSatisifyMultipleObjectWaits(WaitBlock);
507 Status = STATUS_WAIT_0;
508 goto WaitDone;
509 }
510
511 /* Make sure we can satisfy the Alertable request */
512 KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status);
513
514 /* Set the Wait Status */
515 CurrentThread->WaitStatus = Status;
516
517 /* Enable the Timeout Timer if there was any specified */
518 if (Timeout != NULL) {
519
520 /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
521 if (!Timeout->QuadPart) {
522
523 /* Return a timeout */
524 Status = STATUS_TIMEOUT;
525 goto WaitDone;
526 }
527
528 /* Point to Timer Wait Block and Thread Timer */
529 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
530 ThreadTimer = &CurrentThread->Timer;
531
532 /* Connect the Timer Wait Block */
533 WaitBlock->NextWaitBlock = TimerWaitBlock;
534
535 /* Set up the Timer Wait Block */
536 TimerWaitBlock->Object = (PVOID)ThreadTimer;
537 TimerWaitBlock->Thread = CurrentThread;
538 TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
539 TimerWaitBlock->WaitType = WaitAny;
540 TimerWaitBlock->NextWaitBlock = WaitBlockArray;
541
542 /* Link the timer to this Wait Block */
543 InitializeListHead(&ThreadTimer->Header.WaitListHead);
544
545 /* Insert the Timer into the Timer Lists and enable it */
546 if (!KiInsertTimer(ThreadTimer, *Timeout)) {
547
548 /* Return a timeout if we couldn't insert the timer for some reason */
549 Status = STATUS_TIMEOUT;
550 goto WaitDone;
551 }
552 }
553
554 /* Insert into Object's Wait List*/
555 WaitBlock = CurrentThread->WaitBlockList;
556 do {
557
558 /* Get the Current Object */
559 CurrentObject = WaitBlock->Object;
560
561 /* Link the Object to this Wait Block */
562 InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
563
564 /* Move to the next Wait Block */
565 WaitBlock = WaitBlock->NextWaitBlock;
566 } while (WaitBlock != WaitBlockArray);
567
568 /* Handle Kernel Queues */
569 if (CurrentThread->Queue) {
570
571 DPRINT("Waking Queue\n");
572 KiWakeQueue(CurrentThread->Queue);
573 }
574
575 /* Block the Thread */
576 DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread());
577 KiBlockThread(&Status,
578 Alertable,
579 WaitMode,
580 (UCHAR)WaitReason);
581
582 /* Check if we were executing an APC */
583 if (Status != STATUS_KERNEL_APC) {
584
585 /* Return Status */
586 return Status;
587 }
588
589 DPRINT("Looping Again\n");
590 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
591
592 } while (TRUE);
593
594 WaitDone:
595 /* Release the Lock, we are done */
596 DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status);
597 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
598 return Status;
599 }
600
601 VOID
602 FASTCALL
603 KiSatisfyObjectWait(PDISPATCHER_HEADER Object,
604 PKTHREAD Thread)
605
606 {
607 /* Special case for Mutants */
608 if (Object->Type == MutantObject) {
609
610 /* Decrease the Signal State */
611 Object->SignalState--;
612
613 /* Check if it's now non-signaled */
614 if (Object->SignalState == 0) {
615
616 /* Set the Owner Thread */
617 ((PKMUTANT)Object)->OwnerThread = Thread;
618
619 /* Disable APCs if needed */
620 Thread->KernelApcDisable -= ((PKMUTANT)Object)->ApcDisable;
621
622 /* Check if it's abandoned */
623 if (((PKMUTANT)Object)->Abandoned) {
624
625 /* Unabandon it */
626 ((PKMUTANT)Object)->Abandoned = FALSE;
627
628 /* Return Status */
629 Thread->WaitStatus = STATUS_ABANDONED;
630 }
631
632 /* Insert it into the Mutant List */
633 InsertHeadList(&Thread->MutantListHead, &((PKMUTANT)Object)->MutantListEntry);
634 }
635
636 } else if ((Object->Type & TIMER_OR_EVENT_TYPE) == EventSynchronizationObject) {
637
638 /* These guys (Syncronization Timers and Events) just get un-signaled */
639 Object->SignalState = 0;
640
641 } else if (Object->Type == SemaphoreObject) {
642
643 /* These ones can have multiple signalings, so we only decrease it */
644 Object->SignalState--;
645 }
646 }
647
648 VOID
649 FASTCALL
650 KiWaitTest(PDISPATCHER_HEADER Object,
651 KPRIORITY Increment)
652 {
653 PLIST_ENTRY WaitEntry;
654 PLIST_ENTRY WaitList;
655 PKWAIT_BLOCK CurrentWaitBlock;
656 PKWAIT_BLOCK NextWaitBlock;
657 PKTHREAD WaitThread;
658
659 /* Loop the Wait Entries */
660 DPRINT("KiWaitTest for Object: %x\n", Object);
661 WaitList = &Object->WaitListHead;
662 WaitEntry = WaitList->Flink;
663 while ((WaitEntry != WaitList) && (Object->SignalState > 0)) {
664
665 /* Get the current wait block */
666 CurrentWaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
667 WaitThread = CurrentWaitBlock->Thread;
668
669 /* Check the current Wait Mode */
670 if (CurrentWaitBlock->WaitType == WaitAny) {
671
672 /* Easy case, satisfy only this wait */
673 DPRINT("Satisfiying a Wait any\n");
674 WaitEntry = WaitEntry->Blink;
675 KiSatisfyObjectWait(Object, WaitThread);
676
677 } else {
678
679 /* Everything must be satisfied */
680 DPRINT("Checking for a Wait All\n");
681 NextWaitBlock = CurrentWaitBlock->NextWaitBlock;
682
683 /* Loop first to make sure they are valid */
684 while (NextWaitBlock != CurrentWaitBlock) {
685
686 /* Check if the object is signaled */
687 DPRINT("Checking: %x %d\n", NextWaitBlock->Object, Object->SignalState);
688 if (!KiIsObjectSignaled(NextWaitBlock->Object, WaitThread)) {
689
690 /* It's not, move to the next one */
691 DPRINT("One of the object is non-signaled, sorry.\n");
692 goto SkipUnwait;
693 }
694
695 /* Go to the next Wait block */
696 NextWaitBlock = NextWaitBlock->NextWaitBlock;
697 }
698
699 /* All the objects are signaled, we can satisfy */
700 DPRINT("Satisfiying a Wait All\n");
701 WaitEntry = WaitEntry->Blink;
702 KiSatisifyMultipleObjectWaits(CurrentWaitBlock);
703 }
704
705 /* All waits satisfied, unwait the thread */
706 DPRINT("Unwaiting the Thread\n");
707 KiAbortWaitThread(WaitThread, CurrentWaitBlock->WaitKey, Increment);
708
709 SkipUnwait:
710 /* Next entry */
711 WaitEntry = WaitEntry->Flink;
712 }
713
714 DPRINT("Done\n");
715 }
716
717 /* Must be called with the dispatcher lock held */
718 VOID
719 FASTCALL
720 KiAbortWaitThread(PKTHREAD Thread,
721 NTSTATUS WaitStatus,
722 KPRIORITY Increment)
723 {
724 PKWAIT_BLOCK WaitBlock;
725
726 /* If we are blocked, we must be waiting on something also */
727 DPRINT("KiAbortWaitThread: %x, Status: %x, %x \n", Thread, WaitStatus, Thread->WaitBlockList);
728 ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL));
729
730 /* Remove the Wait Blocks from the list */
731 DPRINT("Removing waits\n");
732 WaitBlock = Thread->WaitBlockList;
733 do {
734
735 /* Remove it */
736 DPRINT("Removing Waitblock: %x, %x\n", WaitBlock, WaitBlock->NextWaitBlock);
737 RemoveEntryList(&WaitBlock->WaitListEntry);
738
739 /* Go to the next one */
740 WaitBlock = WaitBlock->NextWaitBlock;
741 } while (WaitBlock != Thread->WaitBlockList);
742
743 /* Check if there's a Thread Timer */
744 if (Thread->Timer.Header.Inserted) {
745
746 /* Cancel the Thread Timer with the no-lock fastpath */
747 DPRINT("Removing the Thread's Timer\n");
748 Thread->Timer.Header.Inserted = FALSE;
749 RemoveEntryList(&Thread->Timer.TimerListEntry);
750 }
751
752 /* Increment the Queue's active threads */
753 if (Thread->Queue) {
754
755 DPRINT("Incrementing Queue's active threads\n");
756 Thread->Queue->CurrentCount++;
757 }
758
759 /* Reschedule the Thread */
760 DPRINT("Unblocking the Thread\n");
761 KiUnblockThread(Thread, &WaitStatus, 0);
762 }
763
764 BOOLEAN
765 inline
766 FASTCALL
767 KiIsObjectSignaled(PDISPATCHER_HEADER Object,
768 PKTHREAD Thread)
769 {
770 /* Mutants are...well...mutants! */
771 if (Object->Type == MutantObject) {
772
773 /*
774 * Because Cutler hates mutants, they are actually signaled if the Signal State is <= 0
775 * Well, only if they are recursivly acquired (i.e if we own it right now).
776 * Of course, they are also signaled if their signal state is 1.
777 */
778 if ((Object->SignalState <= 0 && ((PKMUTANT)Object)->OwnerThread == Thread) ||
779 (Object->SignalState == 1)) {
780
781 /* Signaled Mutant */
782 return (TRUE);
783
784 } else {
785
786 /* Unsignaled Mutant */
787 return (FALSE);
788 }
789 }
790
791 /* Any other object is not a mutated freak, so let's use logic */
792 return (!Object->SignalState <= 0);
793 }
794
795 BOOL
796 inline
797 FASTCALL
798 KiIsObjectWaitable(PVOID Object)
799 {
800 POBJECT_HEADER Header;
801 Header = BODY_TO_HEADER(Object);
802
803 if (Header->ObjectType == ExEventObjectType ||
804 Header->ObjectType == ExIoCompletionType ||
805 Header->ObjectType == ExMutantObjectType ||
806 Header->ObjectType == ExSemaphoreObjectType ||
807 Header->ObjectType == ExTimerType ||
808 Header->ObjectType == PsProcessType ||
809 Header->ObjectType == PsThreadType ||
810 Header->ObjectType == IoFileObjectType) {
811
812 return TRUE;
813
814 } else {
815
816 return FALSE;
817 }
818 }
819
820 VOID
821 inline
822 FASTCALL
823 KiSatisifyMultipleObjectWaits(PKWAIT_BLOCK WaitBlock)
824 {
825 PKWAIT_BLOCK FirstBlock = WaitBlock;
826 PKTHREAD WaitThread = WaitBlock->Thread;
827
828 /* Loop through all the Wait Blocks, and wake each Object */
829 do {
830
831 /* Wake the Object */
832 KiSatisfyObjectWait(WaitBlock->Object, WaitThread);
833 WaitBlock = WaitBlock->NextWaitBlock;
834 } while (WaitBlock != FirstBlock);
835 }
836
837 VOID
838 inline
839 FASTCALL
840 KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
841 ULONG Type,
842 ULONG Size,
843 ULONG SignalState)
844 {
845 Header->Type = (UCHAR)Type;
846 Header->Absolute = 0;
847 Header->Inserted = 0;
848 Header->Size = (UCHAR)Size;
849 Header->SignalState = SignalState;
850 InitializeListHead(&(Header->WaitListHead));
851 }
852
853 KIRQL
854 inline
855 FASTCALL
856 KeAcquireDispatcherDatabaseLock(VOID)
857 {
858 KIRQL OldIrql;
859
860 KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
861 return OldIrql;
862 }
863
864 VOID
865 inline
866 FASTCALL
867 KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
868 {
869 KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
870 }
871
872 VOID
873 inline
874 FASTCALL
875 KeInitializeDispatcher(VOID)
876 {
877 /* Initialize the Dispatcher Lock */
878 KeInitializeSpinLock(&DispatcherDatabaseLock);
879 }
880
881 VOID
882 inline
883 FASTCALL
884 KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
885 {
886 /* If it's the idle thread, dispatch */
887 if (!KeIsExecutingDpc() && OldIrql < DISPATCH_LEVEL && KeGetCurrentThread() != NULL &&
888 KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread) {
889
890 KiDispatchThreadNoLock(THREAD_STATE_READY);
891 KeLowerIrql(OldIrql);
892
893 } else {
894
895 /* Just release the spin lock */
896 KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
897 }
898 }
899
900 VOID
901 inline
902 FASTCALL
903 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
904 {
905 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
906 }
907
908 /* EOF */