Basic support for priority boosting.
[reactos.git] / reactos / ntoskrnl / ke / wait.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS project
4 * FILE: ntoskrnl/ke/wait.c
5 * PURPOSE: Manages non-busy waiting
6 * PROGRAMMER: David Welch (welch@mcmail.com)
7 * REVISION HISTORY:
8 * 21/07/98: Created
9 * 12/1/99: Phillip Susi: Fixed wake code in KeDispatcherObjectWake
10 * so that things like KeWaitForXXX() return the correct value
11 */
12
13 /* NOTES ********************************************************************
14 *
15 */
16
17 /* INCLUDES ******************************************************************/
18
19 #include <ntoskrnl.h>
20
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 /* GLOBALS ******************************************************************/
25
26 static KSPIN_LOCK DispatcherDatabaseLock;
27
28 #define KeDispatcherObjectWakeOne(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, FALSE)
29 #define KeDispatcherObjectWakeAll(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, TRUE)
30
31 extern POBJECT_TYPE EXPORTED ExMutantObjectType;
32 extern POBJECT_TYPE EXPORTED ExSemaphoreObjectType;
33 extern POBJECT_TYPE EXPORTED ExTimerType;
34
35 /* FUNCTIONS *****************************************************************/
36
37 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
38 ULONG Type,
39 ULONG Size,
40 ULONG SignalState)
41 {
42 Header->Type = (UCHAR)Type;
43 Header->Absolute = 0;
44 Header->Inserted = 0;
45 Header->Size = (UCHAR)Size;
46 Header->SignalState = SignalState;
47 InitializeListHead(&(Header->WaitListHead));
48 }
49
50
51 KIRQL
52 KeAcquireDispatcherDatabaseLock(VOID)
53 /*
54 * PURPOSE: Acquires the dispatcher database lock for the caller
55 */
56 {
57 KIRQL OldIrql;
58
59 DPRINT("KeAcquireDispatcherDatabaseLock()\n");
60
61 KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
62 return OldIrql;
63 }
64
65
66 VOID
67 KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
68 /*
69 * PURPOSE: Acquires the dispatcher database lock for the caller
70 */
71 {
72 DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n");
73
74 KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
75 }
76
77
78 VOID
79 KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
80 {
81 DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql);
82 if (!KeIsExecutingDpc() &&
83 OldIrql < DISPATCH_LEVEL &&
84 KeGetCurrentThread() != NULL &&
85 KeGetCurrentThread() == KeGetCurrentKPCR()->PrcbData.IdleThread)
86 {
87 PsDispatchThreadNoLock(THREAD_STATE_READY);
88 KeLowerIrql(OldIrql);
89 }
90 else
91 {
92 KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
93 }
94 }
95
96
97 VOID
98 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
99 {
100 DPRINT("KeReleaseDispatcherDatabaseLock()\n");
101
102 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
103 }
104
105
106 static BOOLEAN
107 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
108 PKTHREAD Thread)
109 /*
110 * FUNCTION: Perform side effects on object before a wait for a thread is
111 * satisfied
112 */
113 {
114 BOOLEAN Abandoned = FALSE;
115
116 switch (hdr->Type)
117 {
118 case InternalSynchronizationEvent:
119 hdr->SignalState = 0;
120 break;
121
122 case InternalQueueType:
123 break;
124
125 case InternalSemaphoreType:
126 hdr->SignalState--;
127 break;
128
129 case InternalProcessType:
130 break;
131
132 case InternalThreadType:
133 break;
134
135 case InternalNotificationEvent:
136 break;
137
138 case InternalSynchronizationTimer:
139 hdr->SignalState = FALSE;
140 break;
141
142 case InternalNotificationTimer:
143 break;
144
145 case InternalMutexType:
146 {
147 PKMUTEX Mutex;
148
149 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
150 hdr->SignalState--;
151 ASSERT(hdr->SignalState <= 1);
152 if (hdr->SignalState == 0)
153 {
154 if (Thread == NULL)
155 {
156 DPRINT("Thread == NULL!\n");
157 KEBUGCHECK(0);
158 }
159 Abandoned = Mutex->Abandoned;
160 if (Thread != NULL)
161 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
162 Mutex->OwnerThread = Thread;
163 Mutex->Abandoned = FALSE;
164 }
165 }
166 break;
167
168 default:
169 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
170 KEBUGCHECK(0);
171 }
172
173 return Abandoned;
174 }
175
176 static BOOLEAN
177 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
178 PKTHREAD Thread)
179 {
180 if (hdr->Type == InternalMutexType)
181 {
182 PKMUTEX Mutex;
183
184 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
185
186 ASSERT(hdr->SignalState <= 1);
187
188 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
189 {
190 return (TRUE);
191 }
192 else
193 {
194 return (FALSE);
195 }
196 }
197
198 if (hdr->SignalState <= 0)
199 {
200 return (FALSE);
201 }
202 else
203 {
204 return (TRUE);
205 }
206 }
207
208 /* Must be called with the dispatcher lock held */
209 BOOLEAN KiAbortWaitThread(PKTHREAD Thread, NTSTATUS WaitStatus)
210 {
211 PKWAIT_BLOCK WaitBlock;
212 BOOLEAN WasWaiting;
213
214 /* if we are blocked, we must be waiting on something also */
215 ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL));
216
217 WaitBlock = (PKWAIT_BLOCK)Thread->WaitBlockList;
218 WasWaiting = (WaitBlock != NULL);
219
220 while (WaitBlock)
221 {
222 RemoveEntryList(&WaitBlock->WaitListEntry);
223 WaitBlock = WaitBlock->NextWaitBlock;
224 }
225
226 Thread->WaitBlockList = NULL;
227
228 if (WasWaiting)
229 {
230 PsUnblockThread((PETHREAD)Thread, &WaitStatus, 0);
231 }
232 return WasWaiting;
233 }
234
235 static BOOLEAN
236 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
237 KPRIORITY increment,
238 BOOLEAN WakeAll)
239 {
240 PKWAIT_BLOCK Waiter;
241 PKWAIT_BLOCK WaiterHead;
242 PLIST_ENTRY EnumEntry;
243 NTSTATUS Status;
244 BOOLEAN Abandoned;
245 BOOLEAN AllSignaled;
246 BOOLEAN WakedAny = FALSE;
247
248 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
249 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
250 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
251
252 if (IsListEmpty(&hdr->WaitListHead))
253 {
254 return (FALSE);
255 }
256
257 //enum waiters for this dispatcher object
258 EnumEntry = hdr->WaitListHead.Flink;
259 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
260 {
261 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
262 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
263 EnumEntry = EnumEntry->Flink;
264 ASSERT(WaiterHead->Thread != NULL);
265 ASSERT(WaiterHead->Thread->WaitBlockList != NULL);
266
267 Abandoned = FALSE;
268
269 if (WaiterHead->WaitType == WaitAny)
270 {
271 DPRINT("WaitAny: Remove all wait blocks.\n");
272 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
273 {
274 RemoveEntryList(&Waiter->WaitListEntry);
275 }
276
277 WaiterHead->Thread->WaitBlockList = NULL;
278
279 /*
280 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
281 * but thats ok since WakeAll objects has no sideeffects.
282 */
283 Abandoned |= KiSideEffectsBeforeWake(hdr, WaiterHead->Thread);
284 }
285 else
286 {
287 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
288
289 AllSignaled = TRUE;
290
291 //all WaitAll obj. for thread need to be signaled to satisfy a wake
292 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
293 {
294 //no need to check hdr since it has to be signaled
295 if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
296 {
297 if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
298 {
299 AllSignaled = FALSE;
300 break;
301 }
302 }
303 }
304
305 if (AllSignaled)
306 {
307 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
308 {
309 RemoveEntryList(&Waiter->WaitListEntry);
310
311 if (Waiter->WaitType == WaitAll)
312 {
313 Abandoned |= KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread);
314 }
315
316 //no WaitAny objects can possibly be signaled since we are here
317 ASSERT(!(Waiter->WaitType == WaitAny
318 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
319 }
320
321 WaiterHead->Thread->WaitBlockList = NULL;
322 }
323 }
324
325 if (WaiterHead->Thread->WaitBlockList == NULL)
326 {
327 Status = WaiterHead->WaitKey;
328 if (Abandoned)
329 {
330 DPRINT("Abandoned mutex among objects");
331 Status += STATUS_ABANDONED_WAIT_0;
332 }
333
334 WakedAny = TRUE;
335 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
336 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb),
337 &Status, increment);
338 }
339 }
340
341 return WakedAny;
342 }
343
344
345 BOOLEAN KiDispatcherObjectWake(DISPATCHER_HEADER* hdr, KPRIORITY increment)
346 /*
347 * FUNCTION: Wake threads waiting on a dispatcher object
348 * NOTE: The exact semantics of waking are dependant on the type of object
349 */
350 {
351 BOOL Ret;
352
353 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
354 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
355 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
356 DPRINT("hdr->Type %x\n",hdr->Type);
357 switch (hdr->Type)
358 {
359 case InternalNotificationEvent:
360 return(KeDispatcherObjectWakeAll(hdr, increment));
361
362 case InternalNotificationTimer:
363 return(KeDispatcherObjectWakeAll(hdr, increment));
364
365 case InternalSynchronizationEvent:
366 return(KeDispatcherObjectWakeOne(hdr, increment));
367
368 case InternalSynchronizationTimer:
369 return(KeDispatcherObjectWakeOne(hdr, increment));
370
371 case InternalQueueType:
372 return(KeDispatcherObjectWakeOne(hdr, increment));
373
374 case InternalSemaphoreType:
375 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
376 if(hdr->SignalState>0)
377 {
378 do
379 {
380 DPRINT("Waking one semaphore waiter\n");
381 Ret = KeDispatcherObjectWakeOne(hdr, increment);
382 } while(hdr->SignalState > 0 && Ret) ;
383 return(Ret);
384 }
385 else return FALSE;
386
387 case InternalProcessType:
388 return(KeDispatcherObjectWakeAll(hdr, increment));
389
390 case InternalThreadType:
391 return(KeDispatcherObjectWakeAll(hdr, increment));
392
393 case InternalMutexType:
394 return(KeDispatcherObjectWakeOne(hdr, increment));
395 }
396 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
397 KEBUGCHECK(0);
398 return(FALSE);
399 }
400
401
402 /*
403 * @implemented
404 */
405 NTSTATUS STDCALL
406 KeWaitForSingleObject(PVOID Object,
407 KWAIT_REASON WaitReason,
408 KPROCESSOR_MODE WaitMode,
409 BOOLEAN Alertable,
410 PLARGE_INTEGER Timeout)
411 /*
412 * FUNCTION: Puts the current thread into a wait state until the
413 * given dispatcher object is set to signalled
414 * ARGUMENTS:
415 * Object = Object to wait on
416 * WaitReason = Reason for the wait (debugging aid)
417 * WaitMode = Can be KernelMode or UserMode, if UserMode then
418 * user-mode APCs can be delivered and the thread's
419 * stack can be paged out
420 * Altertable = Specifies if the wait is a alertable
421 * Timeout = Optional timeout value
422 * RETURNS: Status
423 */
424 {
425 return KeWaitForMultipleObjects(1,
426 &Object,
427 WaitAny,
428 WaitReason,
429 WaitMode,
430 Alertable,
431 Timeout,
432 NULL);
433 }
434
435
436 inline
437 PVOID
438 KiGetWaitableObjectFromObject(PVOID Object)
439 {
440 //special case when waiting on file objects
441 if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
442 {
443 return &((PFILE_OBJECT)Object)->Event;
444 }
445
446 return Object;
447 }
448
449
450 inline BOOL
451 KiIsObjectWaitable(PVOID Object)
452 {
453 POBJECT_HEADER Header;
454 Header = BODY_TO_HEADER(Object);
455 if (Header->ObjectType == ExEventObjectType ||
456 Header->ObjectType == ExIoCompletionType ||
457 Header->ObjectType == ExMutantObjectType ||
458 Header->ObjectType == ExSemaphoreObjectType ||
459 Header->ObjectType == ExTimerType ||
460 Header->ObjectType == PsProcessType ||
461 Header->ObjectType == PsThreadType ||
462 Header->ObjectType == IoFileObjectType)
463 {
464 return TRUE;
465 }
466 else
467 {
468 return FALSE;
469 }
470 }
471
472 /*
473 * @implemented
474 */
475 NTSTATUS STDCALL
476 KeWaitForMultipleObjects(ULONG Count,
477 PVOID Object[],
478 WAIT_TYPE WaitType,
479 KWAIT_REASON WaitReason,
480 KPROCESSOR_MODE WaitMode,
481 BOOLEAN Alertable,
482 PLARGE_INTEGER Timeout,
483 PKWAIT_BLOCK WaitBlockArray)
484 {
485 DISPATCHER_HEADER *hdr;
486 PKWAIT_BLOCK blk;
487 PKTHREAD CurrentThread;
488 ULONG CountSignaled;
489 ULONG i;
490 NTSTATUS Status;
491 KIRQL OldIrql;
492 BOOLEAN Abandoned;
493
494 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
495 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
496
497 ASSERT(0 < Count && Count <= MAXIMUM_WAIT_OBJECTS);
498
499 CurrentThread = KeGetCurrentThread();
500
501 /*
502 * Work out where we are going to put the wait blocks
503 */
504 if (WaitBlockArray == NULL)
505 {
506 if (Count > THREAD_WAIT_OBJECTS)
507 {
508 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
509 return (STATUS_UNSUCCESSFUL);
510 }
511 WaitBlockArray = &CurrentThread->WaitBlock[0];
512 }
513 else
514 {
515 if (Count > MAXIMUM_WAIT_OBJECTS)
516 {
517 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
518 return (STATUS_UNSUCCESSFUL);
519 }
520 }
521
522
523
524 /*
525 * Set up the timeout if required
526 */
527 if (Timeout != NULL && Timeout->QuadPart != 0)
528 {
529 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
530 }
531
532 do
533 {
534 if (CurrentThread->WaitNext)
535 {
536 CurrentThread->WaitNext = FALSE;
537 OldIrql = CurrentThread->WaitIrql;
538 }
539 else
540 {
541 OldIrql = KeAcquireDispatcherDatabaseLock ();
542 }
543
544 /* Alertability 101
545 * ----------------
546 * A Wait can either be Alertable, or Non-Alertable.
547 * An Alertable Wait means that APCs can "Wake" the Thread, also called UnWaiting
548 * If an APC is Pending however, we must refuse an Alertable Wait. Such a wait would
549 * be pointless since an APC is just about to be delivered.
550 *
551 * There are many ways to check if it's safe to be alertable, and these are the ones
552 * that I could think of:
553 * - The Thread is already Alerted. So someone beat us to the punch and we bail out.
554 * - The Thread is Waiting in User-Mode, the APC Queue is not-empty.
555 * It's defintely clear that we have incoming APCs, so we need to bail out and let the system
556 * know that there are Pending User APCs (so they can be Delivered and maybe we can try again)
557 *
558 * Furthermore, wether or not we want to be Alertable, if the Thread is waiting in User-Mode, and there
559 * are Pending User APCs, we should bail out, since APCs will be delivered any second.
560 */
561 if (Alertable) {
562 if (CurrentThread->Alerted[(int)WaitMode]) {
563 CurrentThread->Alerted[(int)WaitMode] = FALSE;
564 DPRINT("Alertability failed\n");
565 KeReleaseDispatcherDatabaseLock(OldIrql);
566 return (STATUS_ALERTED);
567 } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) && (WaitMode == UserMode)) {
568 DPRINT1("Alertability failed\n");
569 CurrentThread->ApcState.UserApcPending = TRUE;
570 KeReleaseDispatcherDatabaseLock(OldIrql);
571 return (STATUS_USER_APC);
572 }
573 } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode != KernelMode)) {
574 DPRINT1("Alertability failed\n");
575 KeReleaseDispatcherDatabaseLock(OldIrql);
576 return (STATUS_USER_APC);
577 }
578
579 /*
580 * Check if the wait is (already) satisfied
581 */
582 CountSignaled = 0;
583 Abandoned = FALSE;
584 for (i = 0; i < Count; i++)
585 {
586 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
587
588 if (KiIsObjectSignalled(hdr, CurrentThread))
589 {
590 CountSignaled++;
591
592 if (WaitType == WaitAny)
593 {
594 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
595
596 if (Timeout != NULL && Timeout->QuadPart != 0)
597 {
598 KeCancelTimer(&CurrentThread->Timer);
599 }
600
601 KeReleaseDispatcherDatabaseLock(OldIrql);
602
603 DPRINT("One object is (already) signaled!\n");
604 if (Abandoned == TRUE)
605 {
606 return (STATUS_ABANDONED_WAIT_0 + i);
607 }
608
609 return (STATUS_WAIT_0 + i);
610 }
611 }
612 }
613
614 Abandoned = FALSE;
615 if ((WaitType == WaitAll) && (CountSignaled == Count))
616 {
617 for (i = 0; i < Count; i++)
618 {
619 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
620 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
621 }
622
623 if (Timeout != NULL && Timeout->QuadPart != 0)
624 {
625 KeCancelTimer(&CurrentThread->Timer);
626 }
627
628 KeReleaseDispatcherDatabaseLock(OldIrql);
629 DPRINT("All objects are (already) signaled!\n");
630
631 if (Abandoned == TRUE)
632 {
633 return (STATUS_ABANDONED_WAIT_0);
634 }
635
636 return (STATUS_WAIT_0);
637 }
638
639 //zero timeout is used for testing if the object(s) can be immediately acquired
640 if (Timeout != NULL && Timeout->QuadPart == 0)
641 {
642 KeReleaseDispatcherDatabaseLock(OldIrql);
643 return STATUS_TIMEOUT;
644 }
645
646 /*
647 * Check if we have already timed out
648 */
649 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
650 {
651 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
652 KeCancelTimer(&CurrentThread->Timer);
653 KeReleaseDispatcherDatabaseLock(OldIrql);
654 return (STATUS_TIMEOUT);
655 }
656
657 /* Append wait block to the KTHREAD wait block list */
658 CurrentThread->WaitBlockList = blk = WaitBlockArray;
659
660 /*
661 * Set up the wait
662 */
663 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
664
665 for (i = 0; i < Count; i++)
666 {
667 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
668
669 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
670 blk->Thread = CurrentThread;
671 blk->WaitKey = (USHORT)(STATUS_WAIT_0 + i);
672 blk->WaitType = (USHORT)WaitType;
673
674 if (i == (Count - 1))
675 {
676 if (Timeout != NULL)
677 {
678 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
679 }
680 else
681 {
682 blk->NextWaitBlock = NULL;
683 }
684 }
685 else
686 {
687 blk->NextWaitBlock = blk + 1;
688 }
689
690 /*
691 * add wait block to disp. obj. wait list
692 * Use FIFO for all waits except for queues which use LIFO
693 */
694 if (WaitReason == WrQueue)
695 {
696 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
697 }
698 else
699 {
700 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
701 }
702
703 blk = blk->NextWaitBlock;
704 }
705
706 if (Timeout != NULL)
707 {
708 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
709 CurrentThread->WaitBlock[3].Thread = CurrentThread;
710 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
711 CurrentThread->WaitBlock[3].WaitType = WaitAny;
712 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
713
714 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
715 &CurrentThread->WaitBlock[3].WaitListEntry);
716 }
717
718 //kernel queues
719 if (CurrentThread->Queue && WaitReason != WrQueue)
720 {
721 DPRINT("queue: sleep on something else\n");
722 CurrentThread->Queue->CurrentCount--;
723
724 //wake another thread
725 if (CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
726 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
727 {
728 KiDispatcherObjectWake(&CurrentThread->Queue->Header, IO_NO_INCREMENT);
729 }
730 }
731
732 PsBlockThread(&Status, Alertable, WaitMode, TRUE, OldIrql, (UCHAR)WaitReason);
733
734 //kernel queues
735 OldIrql = KeAcquireDispatcherDatabaseLock ();
736 if (CurrentThread->Queue && WaitReason != WrQueue)
737 {
738 DPRINT("queue: wake from something else\n");
739 CurrentThread->Queue->CurrentCount++;
740 }
741 if (Status == STATUS_KERNEL_APC)
742 {
743 CurrentThread->WaitNext = TRUE;
744 CurrentThread->WaitIrql = OldIrql;
745 }
746 else
747 {
748 KeReleaseDispatcherDatabaseLock(OldIrql);
749 }
750
751 } while (Status == STATUS_KERNEL_APC);
752
753
754 if (Timeout != NULL)
755 {
756 KeCancelTimer(&CurrentThread->Timer);
757 }
758
759 DPRINT("Returning from KeWaitForMultipleObjects()\n");
760 return (Status);
761 }
762
763 VOID KeInitializeDispatcher(VOID)
764 {
765 KeInitializeSpinLock(&DispatcherDatabaseLock);
766 }
767
768 NTSTATUS STDCALL
769 NtWaitForMultipleObjects(IN ULONG Count,
770 IN HANDLE Object [],
771 IN WAIT_TYPE WaitType,
772 IN BOOLEAN Alertable,
773 IN PLARGE_INTEGER UnsafeTime)
774 {
775 KWAIT_BLOCK WaitBlockArray[MAXIMUM_WAIT_OBJECTS];
776 PVOID ObjectPtrArray[MAXIMUM_WAIT_OBJECTS];
777 NTSTATUS Status;
778 ULONG i, j;
779 KPROCESSOR_MODE WaitMode;
780 LARGE_INTEGER Time;
781
782 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
783 "Time %x)\n", Count,Object,Alertable,Time);
784
785 if (Count > MAXIMUM_WAIT_OBJECTS)
786 return STATUS_UNSUCCESSFUL;
787 if (0 == Count)
788 return STATUS_INVALID_PARAMETER;
789
790 if (UnsafeTime)
791 {
792 Status = MmCopyFromCaller(&Time, UnsafeTime, sizeof(LARGE_INTEGER));
793 if (!NT_SUCCESS(Status))
794 {
795 return(Status);
796 }
797 }
798
799 WaitMode = ExGetPreviousMode();
800
801 /* reference all objects */
802 for (i = 0; i < Count; i++)
803 {
804 Status = ObReferenceObjectByHandle(Object[i],
805 SYNCHRONIZE,
806 NULL,
807 WaitMode,
808 &ObjectPtrArray[i],
809 NULL);
810 if (!NT_SUCCESS(Status) || !KiIsObjectWaitable(ObjectPtrArray[i]))
811 {
812 if (NT_SUCCESS(Status))
813 {
814 DPRINT1("Waiting for object type '%wZ' is not supported\n",
815 &BODY_TO_HEADER(ObjectPtrArray[i])->ObjectType->TypeName);
816 Status = STATUS_HANDLE_NOT_WAITABLE;
817 i++;
818 }
819 /* dereference all referenced objects */
820 for (j = 0; j < i; j++)
821 {
822 ObDereferenceObject(ObjectPtrArray[j]);
823 }
824
825 return(Status);
826 }
827 }
828
829 Status = KeWaitForMultipleObjects(Count,
830 ObjectPtrArray,
831 WaitType,
832 UserRequest,
833 WaitMode,
834 Alertable,
835 UnsafeTime ? &Time : NULL,
836 WaitBlockArray);
837
838 /* dereference all objects */
839 for (i = 0; i < Count; i++)
840 {
841 ObDereferenceObject(ObjectPtrArray[i]);
842 }
843
844 return(Status);
845 }
846
847
848 /*
849 * @implemented
850 */
851 NTSTATUS STDCALL
852 NtWaitForSingleObject(IN HANDLE Object,
853 IN BOOLEAN Alertable,
854 IN PLARGE_INTEGER UnsafeTime)
855 {
856 PVOID ObjectPtr;
857 NTSTATUS Status;
858 KPROCESSOR_MODE WaitMode;
859 LARGE_INTEGER Time;
860
861 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
862 Object,Alertable,Time);
863
864 if (UnsafeTime)
865 {
866 Status = MmCopyFromCaller(&Time, UnsafeTime, sizeof(LARGE_INTEGER));
867 if (!NT_SUCCESS(Status))
868 {
869 return(Status);
870 }
871 }
872
873 WaitMode = ExGetPreviousMode();
874
875 Status = ObReferenceObjectByHandle(Object,
876 SYNCHRONIZE,
877 NULL,
878 WaitMode,
879 &ObjectPtr,
880 NULL);
881 if (!NT_SUCCESS(Status))
882 {
883 return(Status);
884 }
885 if (!KiIsObjectWaitable(ObjectPtr))
886 {
887 DPRINT1("Waiting for object type '%wZ' is not supported\n",
888 &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName);
889 Status = STATUS_HANDLE_NOT_WAITABLE;
890 }
891 else
892 {
893 Status = KeWaitForSingleObject(ObjectPtr,
894 UserRequest,
895 WaitMode,
896 Alertable,
897 UnsafeTime ? &Time : NULL);
898 }
899
900 ObDereferenceObject(ObjectPtr);
901
902 return(Status);
903 }
904
905
906 NTSTATUS STDCALL
907 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
908 IN HANDLE WaitObject,
909 IN BOOLEAN Alertable,
910 IN PLARGE_INTEGER Time)
911 {
912 KPROCESSOR_MODE WaitMode;
913 DISPATCHER_HEADER* hdr;
914 PVOID SignalObj;
915 PVOID WaitObj;
916 NTSTATUS Status;
917
918 WaitMode = ExGetPreviousMode();
919 Status = ObReferenceObjectByHandle(SignalObject,
920 0,
921 NULL,
922 WaitMode,
923 &SignalObj,
924 NULL);
925 if (!NT_SUCCESS(Status))
926 {
927 return Status;
928 }
929
930 Status = ObReferenceObjectByHandle(WaitObject,
931 SYNCHRONIZE,
932 NULL,
933 WaitMode,
934 &WaitObj,
935 NULL);
936 if (!NT_SUCCESS(Status))
937 {
938 ObDereferenceObject(SignalObj);
939 return Status;
940 }
941
942 hdr = (DISPATCHER_HEADER *)SignalObj;
943 switch (hdr->Type)
944 {
945 case InternalNotificationEvent:
946 case InternalSynchronizationEvent:
947 KeSetEvent(SignalObj,
948 EVENT_INCREMENT,
949 TRUE);
950 break;
951
952 case InternalMutexType:
953 KeReleaseMutex(SignalObj,
954 TRUE);
955 break;
956
957 case InternalSemaphoreType:
958 KeReleaseSemaphore(SignalObj,
959 SEMAPHORE_INCREMENT,
960 1,
961 TRUE);
962 break;
963
964 default:
965 ObDereferenceObject(SignalObj);
966 ObDereferenceObject(WaitObj);
967 return STATUS_OBJECT_TYPE_MISMATCH;
968 }
969
970 Status = KeWaitForSingleObject(WaitObj,
971 UserRequest,
972 WaitMode,
973 Alertable,
974 Time);
975
976 ObDereferenceObject(SignalObj);
977 ObDereferenceObject(WaitObj);
978
979 return Status;
980 }