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