Big merge: thanks alex and greatlord. Not a complete merge but most
[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 /* Remove the thread from the wait list! */
147 if (Thread->WaitListEntry.Flink) 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 BOOLEAN Swappable;
278 PLARGE_INTEGER OriginalDueTime = Interval;
279 LARGE_INTEGER DueTime, NewDueTime;
280
281 /* Check if the lock is already held */
282 if (CurrentThread->WaitNext)
283 {
284 /* Lock is held, disable Wait Next */
285 CurrentThread->WaitNext = FALSE;
286 }
287 else
288 {
289 /* Lock not held, acquire it */
290 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
291 }
292
293 /* Use built-in Wait block */
294 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
295
296 /* Start Wait Loop */
297 do
298 {
299 /* Check if a kernel APC is pending and we're below APC_LEVEL */
300 if ((CurrentThread->ApcState.KernelApcPending) &&
301 !(CurrentThread->SpecialApcDisable) &&
302 (CurrentThread->WaitIrql < APC_LEVEL))
303 {
304 /* Unlock the dispatcher */
305 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
306 }
307 else
308 {
309 /* Check if we can do an alertable wait, if requested */
310 KiCheckAlertability();
311
312 /* Check if we can swap the thread's stack */
313 CurrentThread->WaitListEntry.Flink = NULL;
314 KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
315
316 /* Set status */
317 CurrentThread->WaitStatus = STATUS_WAIT_0;
318
319 /* Set Timer */
320 ThreadTimer = &CurrentThread->Timer;
321
322 /* Setup the Wait Block */
323 CurrentThread->WaitBlockList = TimerWaitBlock;
324 TimerWaitBlock->NextWaitBlock = TimerWaitBlock;
325
326 /* Link the timer to this Wait Block */
327 ThreadTimer->Header.WaitListHead.Flink =
328 &TimerWaitBlock->WaitListEntry;
329 ThreadTimer->Header.WaitListHead.Blink =
330 &TimerWaitBlock->WaitListEntry;
331
332 /* Insert the Timer into the Timer Lists and enable it */
333 if (!KiInsertTimer(ThreadTimer, *Interval))
334 {
335 /* FIXME: We should find a new ready thread */
336 WaitStatus = STATUS_SUCCESS;
337 break;
338 }
339
340 /* Save due time */
341 DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
342
343 /* Handle Kernel Queues */
344 if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
345
346 /* Setup the wait information */
347 CurrentThread->Alertable = Alertable;
348 CurrentThread->WaitMode = WaitMode;
349 CurrentThread->WaitReason = DelayExecution;
350 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
351 CurrentThread->State = Waiting;
352
353 /* Find a new thread to run */
354 KiAddThreadToWaitList(CurrentThread, Swappable);
355 WaitStatus = KiSwapThread();
356 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
357
358 /* Check if we were executing an APC or if we timed out */
359 if (WaitStatus != STATUS_KERNEL_APC)
360 {
361 /* This is a good thing */
362 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
363
364 /* Return Status */
365 return WaitStatus;
366 }
367
368 /* Recalculate due times */
369 Interval = KiRecalculateDueTime(OriginalDueTime,
370 &DueTime,
371 &NewDueTime);
372 }
373
374 /* Acquire again the lock */
375 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
376 } while (TRUE);
377
378 /* Release the Lock, we are done */
379 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
380 return WaitStatus;
381 }
382
383 /*
384 * @implemented
385 */
386 NTSTATUS
387 NTAPI
388 KeWaitForSingleObject(IN PVOID Object,
389 IN KWAIT_REASON WaitReason,
390 IN KPROCESSOR_MODE WaitMode,
391 IN BOOLEAN Alertable,
392 IN PLARGE_INTEGER Timeout OPTIONAL)
393 {
394 PKMUTANT CurrentObject;
395 PKWAIT_BLOCK WaitBlock;
396 PKWAIT_BLOCK TimerWaitBlock;
397 PKTIMER ThreadTimer;
398 PKTHREAD CurrentThread = KeGetCurrentThread();
399 NTSTATUS WaitStatus = STATUS_SUCCESS;
400 BOOLEAN Swappable;
401 LARGE_INTEGER DueTime, NewDueTime;
402 PLARGE_INTEGER OriginalDueTime = Timeout;
403
404 /* Check if the lock is already held */
405 if (CurrentThread->WaitNext)
406 {
407 /* Lock is held, disable Wait Next */
408 CurrentThread->WaitNext = FALSE;
409 }
410 else
411 {
412 /* Lock not held, acquire it */
413 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
414 }
415
416 /* Start the actual Loop */
417 WaitBlock = &CurrentThread->WaitBlock[0];
418 do
419 {
420 /* Check if a kernel APC is pending and we're below APC_LEVEL */
421 if ((CurrentThread->ApcState.KernelApcPending) &&
422 !(CurrentThread->SpecialApcDisable) &&
423 (CurrentThread->WaitIrql < APC_LEVEL))
424 {
425 /* Unlock the dispatcher */
426 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
427 }
428 else
429 {
430 /* Set default status */
431 CurrentThread->WaitStatus = STATUS_WAIT_0;
432
433 /* Get the Current Object */
434 CurrentObject = (PKMUTANT)Object;
435
436 /* Check if it's a mutant */
437 if (CurrentObject->Header.Type == MutantObject)
438 {
439 /* Check its signal state or if we own it */
440 if ((CurrentObject->Header.SignalState > 0) ||
441 (CurrentThread == CurrentObject->OwnerThread))
442 {
443 /* Just unwait this guy and exit */
444 if (CurrentObject->Header.SignalState != (LONG)MINLONG)
445 {
446 /* It has a normal signal state. Unwait and return */
447 KiSatisfyMutantWait(CurrentObject, CurrentThread);
448 WaitStatus = CurrentThread->WaitStatus;
449 goto DontWait;
450 }
451 else
452 {
453 /* Raise an exception (see wasm.ru) */
454 KeReleaseDispatcherDatabaseLock(CurrentThread->
455 WaitIrql);
456 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
457 }
458 }
459 }
460 else if (CurrentObject->Header.SignalState > 0)
461 {
462 /* Another satisfied object */
463 KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
464 WaitStatus = STATUS_WAIT_0;
465 goto DontWait;
466 }
467
468 /* Append wait block to the KTHREAD wait block list */
469 CurrentThread->WaitBlockList = WaitBlock;
470
471 /* Set up the Wait Block */
472 WaitBlock->Object = CurrentObject;
473 WaitBlock->WaitKey = (USHORT)(STATUS_SUCCESS);
474 WaitBlock->WaitType = WaitAny;
475
476 /* Make sure we can satisfy the Alertable request */
477 KiCheckAlertability();
478
479 /* Check if we can swap the thread's stack */
480 CurrentThread->WaitListEntry.Flink = NULL;
481 KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
482
483 /* Enable the Timeout Timer if there was any specified */
484 if (Timeout)
485 {
486 /* Fail if the timeout interval is actually 0 */
487 if (!Timeout->QuadPart)
488 {
489 /* Return a timeout */
490 WaitStatus = STATUS_TIMEOUT;
491 goto DontWait;
492 }
493
494 /* Point to Timer Wait Block and Thread Timer */
495 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
496 ThreadTimer = &CurrentThread->Timer;
497
498 /* Connect the Timer Wait Block */
499 WaitBlock->NextWaitBlock = TimerWaitBlock;
500
501 /* Set up the Timer Wait Block */
502 TimerWaitBlock->NextWaitBlock = WaitBlock;
503
504 /* Link the timer to this Wait Block */
505 ThreadTimer->Header.WaitListHead.Flink =
506 &TimerWaitBlock->WaitListEntry;
507 ThreadTimer->Header.WaitListHead.Blink =
508 &TimerWaitBlock->WaitListEntry;
509
510 /* Insert the Timer into the Timer Lists and enable it */
511 if (!KiInsertTimer(ThreadTimer, *Timeout))
512 {
513 /* Return a timeout if we couldn't insert the timer */
514 WaitStatus = STATUS_TIMEOUT;
515 goto DontWait;
516 }
517
518 /* Set the current due time */
519 DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
520 }
521 else
522 {
523 /* No timer block, so just set our wait block as next */
524 WaitBlock->NextWaitBlock = WaitBlock;
525 }
526
527 /* Link the Object to this Wait Block */
528 InsertTailList(&CurrentObject->Header.WaitListHead,
529 &WaitBlock->WaitListEntry);
530
531 /* Handle Kernel Queues */
532 if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
533
534 /* Setup the wait information */
535 CurrentThread->Alertable = Alertable;
536 CurrentThread->WaitMode = WaitMode;
537 CurrentThread->WaitReason = WaitReason;
538 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
539 CurrentThread->State = Waiting;
540
541 /* Find a new thread to run */
542 KiAddThreadToWaitList(CurrentThread, Swappable);
543 WaitStatus = KiSwapThread();
544 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
545
546 /* Check if we were executing an APC */
547 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
548
549 /* Check if we had a timeout */
550 if (Timeout)
551 {
552 /* Recalculate due times */
553 Timeout = KiRecalculateDueTime(OriginalDueTime,
554 &DueTime,
555 &NewDueTime);
556 }
557 }
558
559 /* Acquire again the lock */
560 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
561 } while (TRUE);
562
563 /* Release the Lock, we are done */
564 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
565 return WaitStatus;
566
567 DontWait:
568 /* Adjust the Quantum */
569 KiAdjustQuantumThread(CurrentThread);
570
571 /* Release & Return */
572 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
573 return WaitStatus;
574 }
575
576 /*
577 * @implemented
578 */
579 NTSTATUS
580 NTAPI
581 KeWaitForMultipleObjects(IN ULONG Count,
582 IN PVOID Object[],
583 IN WAIT_TYPE WaitType,
584 IN KWAIT_REASON WaitReason,
585 IN KPROCESSOR_MODE WaitMode,
586 IN BOOLEAN Alertable,
587 IN PLARGE_INTEGER Timeout OPTIONAL,
588 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
589 {
590 PKMUTANT CurrentObject;
591 PKWAIT_BLOCK WaitBlock;
592 PKWAIT_BLOCK TimerWaitBlock;
593 PKTIMER ThreadTimer;
594 PKTHREAD CurrentThread = KeGetCurrentThread();
595 ULONG AllObjectsSignaled;
596 ULONG WaitIndex;
597 NTSTATUS WaitStatus = STATUS_SUCCESS;
598 BOOLEAN Swappable;
599 PLARGE_INTEGER OriginalDueTime = Timeout;
600 LARGE_INTEGER DueTime, NewDueTime;
601
602 /* Set the Current Thread */
603 CurrentThread = KeGetCurrentThread();
604
605 /* Check if the lock is already held */
606 if (CurrentThread->WaitNext)
607 {
608 /* Lock is held, disable Wait Next */
609 CurrentThread->WaitNext = FALSE;
610 }
611 else
612 {
613 /* Lock not held, acquire it */
614 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
615 }
616
617 /* Make sure the Wait Count is valid */
618 if (!WaitBlockArray)
619 {
620 /* Check in regards to the Thread Object Limit */
621 if (Count > THREAD_WAIT_OBJECTS)
622 {
623 /* Bugcheck */
624 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
625 }
626
627 /* Use the Thread's Wait Block */
628 WaitBlockArray = &CurrentThread->WaitBlock[0];
629 }
630 else
631 {
632 /* Using our own Block Array, so check with the System Object Limit */
633 if (Count > MAXIMUM_WAIT_OBJECTS)
634 {
635 /* Bugcheck */
636 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
637 }
638 }
639
640 /* Start the actual Loop */
641 do
642 {
643 /* Check if a kernel APC is pending and we're below APC_LEVEL */
644 if ((CurrentThread->ApcState.KernelApcPending) &&
645 !(CurrentThread->SpecialApcDisable) &&
646 (CurrentThread->WaitIrql < APC_LEVEL))
647 {
648 /* Unlock the dispatcher */
649 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
650 }
651 else
652 {
653 /* Append wait block to the KTHREAD wait block list */
654 CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
655
656 /* Set default wait status */
657 CurrentThread->WaitStatus = STATUS_WAIT_0;
658
659 /* Check if the wait is (already) satisfied */
660 AllObjectsSignaled = TRUE;
661
662 /* First, we'll try to satisfy the wait directly */
663 for (WaitIndex = 0; WaitIndex < Count; WaitIndex++)
664 {
665 /* Get the Current Object */
666 CurrentObject = (PKMUTANT)Object[WaitIndex];
667
668 /* Check the type of wait */
669 if (WaitType == WaitAny)
670 {
671 /* Check if the Object is a mutant */
672 if (CurrentObject->Header.Type == MutantObject)
673 {
674 /* Check if it's signaled */
675 if ((CurrentObject->Header.SignalState > 0) ||
676 (CurrentThread == CurrentObject->OwnerThread))
677 {
678 /* This is a Wait Any, so unwait this and exit */
679 if (CurrentObject->Header.SignalState !=
680 (LONG)MINLONG)
681 {
682 /* Normal signal state, unwait it and return */
683 KiSatisfyMutantWait(CurrentObject,
684 CurrentThread);
685 WaitStatus = CurrentThread->WaitStatus |
686 WaitIndex;
687 goto DontWait;
688 }
689 else
690 {
691 /* Raise an exception (see wasm.ru) */
692 KeReleaseDispatcherDatabaseLock(CurrentThread->
693 WaitIrql);
694 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
695 }
696 }
697 }
698 else if (CurrentObject->Header.SignalState > 0)
699 {
700 /* Another signaled object, unwait and return */
701 KiSatisfyNonMutantWait(CurrentObject, CurrentThread);
702 WaitStatus = WaitIndex;
703 goto DontWait;
704 }
705 }
706 else
707 {
708 /* Check if we're dealing with a mutant again */
709 if (CurrentObject->Header.Type == MutantObject)
710 {
711 /* Check if it has an invalid count */
712 if ((CurrentThread == CurrentObject->OwnerThread) &&
713 (CurrentObject->Header.SignalState == MINLONG))
714 {
715 /* Raise an exception */
716 KeReleaseDispatcherDatabaseLock(CurrentThread->
717 WaitIrql);
718 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
719 }
720 else if ((CurrentObject->Header.SignalState <= 0) &&
721 (CurrentThread != CurrentObject->OwnerThread))
722 {
723 /* We don't own it, can't satisfy the wait */
724 AllObjectsSignaled = FALSE;
725 }
726 }
727 else if (CurrentObject->Header.SignalState <= 0)
728 {
729 /* Not signaled, can't satisfy */
730 AllObjectsSignaled = FALSE;
731 }
732 }
733
734 /* Set up a Wait Block for this Object */
735 WaitBlock->Object = CurrentObject;
736 WaitBlock->Thread = CurrentThread;
737 WaitBlock->WaitKey = (USHORT)WaitIndex;
738 WaitBlock->WaitType = (USHORT)WaitType;
739 WaitBlock->NextWaitBlock = WaitBlock + 1;
740
741 /* Move to the next Wait Block */
742 WaitBlock = WaitBlock->NextWaitBlock;
743 }
744
745 /* Return to the Root Wait Block */
746 WaitBlock--;
747 WaitBlock->NextWaitBlock = WaitBlockArray;
748
749 /* Check if this is a Wait All and all the objects are signaled */
750 if ((WaitType == WaitAll) && (AllObjectsSignaled))
751 {
752 /* Return to the Root Wait Block */
753 WaitBlock = CurrentThread->WaitBlockList;
754
755 /* Satisfy their Waits and return to the caller */
756 KiWaitSatisfyAll(WaitBlock);
757 WaitStatus = CurrentThread->WaitStatus;
758 goto DontWait;
759 }
760
761 /* Make sure we can satisfy the Alertable request */
762 KiCheckAlertability();
763
764 /* Check if we can swap the thread's stack */
765 CurrentThread->WaitListEntry.Flink = NULL;
766 KiCheckThreadStackSwap(WaitMode, CurrentThread, Swappable);
767
768 /* Enable the Timeout Timer if there was any specified */
769 if (Timeout)
770 {
771 /* Make sure the timeout interval isn't actually 0 */
772 if (!Timeout->QuadPart)
773 {
774 /* Return a timeout */
775 WaitStatus = STATUS_TIMEOUT;
776 goto DontWait;
777 }
778
779 /* Point to Timer Wait Block and Thread Timer */
780 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
781 ThreadTimer = &CurrentThread->Timer;
782
783 /* Connect the Timer Wait Block */
784 WaitBlock->NextWaitBlock = TimerWaitBlock;
785
786 /* Set up the Timer Wait Block */
787 TimerWaitBlock->NextWaitBlock = WaitBlockArray;
788
789 /* Initialize the list head */
790 InitializeListHead(&ThreadTimer->Header.WaitListHead);
791
792 /* Insert the Timer into the Timer Lists and enable it */
793 if (!KiInsertTimer(ThreadTimer, *Timeout))
794 {
795 /* Return a timeout if we couldn't insert the timer */
796 WaitStatus = STATUS_TIMEOUT;
797 goto DontWait;
798 }
799
800 /* Set the current due time */
801 DueTime.QuadPart = ThreadTimer->DueTime.QuadPart;
802 }
803
804 /* Insert into Object's Wait List*/
805 WaitBlock = CurrentThread->WaitBlockList;
806 do
807 {
808 /* Get the Current Object */
809 CurrentObject = WaitBlock->Object;
810
811 /* Link the Object to this Wait Block */
812 InsertTailList(&CurrentObject->Header.WaitListHead,
813 &WaitBlock->WaitListEntry);
814
815 /* Move to the next Wait Block */
816 WaitBlock = WaitBlock->NextWaitBlock;
817 } while (WaitBlock != WaitBlockArray);
818
819 /* Handle Kernel Queues */
820 if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
821
822 /* Setup the wait information */
823 CurrentThread->Alertable = Alertable;
824 CurrentThread->WaitMode = WaitMode;
825 CurrentThread->WaitReason = WaitReason;
826 CurrentThread->WaitTime = ((PLARGE_INTEGER)&KeTickCount)->LowPart;
827 CurrentThread->State = Waiting;
828
829 /* Find a new thread to run */
830 KiAddThreadToWaitList(CurrentThread, Swappable);
831 WaitStatus = KiSwapThread();
832 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
833
834 /* Check if we were executing an APC */
835 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
836
837 /* Check if we had a timeout */
838 if (Timeout)
839 {
840 /* Recalculate due times */
841 Timeout = KiRecalculateDueTime(OriginalDueTime,
842 &DueTime,
843 &NewDueTime);
844 }
845
846 /* Acquire again the lock */
847 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
848 }
849 } while (TRUE);
850
851 /* Release the Lock, we are done */
852 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
853 return WaitStatus;
854
855 DontWait:
856 /* Adjust the Quantum */
857 KiAdjustQuantumThread(CurrentThread);
858
859 /* Release & Return */
860 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
861 return WaitStatus;
862 }
863
864 /* EOF */