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