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