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