2004-08-15 Casper S. Hornstrup <chorns@users.sourceforge.net>
[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) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
29 #define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
30
31 extern POBJECT_TYPE EXPORTED ExMutantObjectType;
32 extern POBJECT_TYPE EXPORTED ExSemaphoreType;
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
83 KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
84 }
85
86
87 VOID
88 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
89 {
90 DPRINT("KeReleaseDispatcherDatabaseLock()\n");
91
92 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
93 }
94
95
96 static BOOLEAN
97 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
98 PKTHREAD Thread)
99 /*
100 * FUNCTION: Perform side effects on object before a wait for a thread is
101 * satisfied
102 */
103 {
104 BOOLEAN Abandoned = FALSE;
105
106 switch (hdr->Type)
107 {
108 case InternalSynchronizationEvent:
109 hdr->SignalState = 0;
110 break;
111
112 case InternalQueueType:
113 case InternalSemaphoreType:
114 hdr->SignalState--;
115 break;
116
117 case InternalProcessType:
118 break;
119
120 case InternalThreadType:
121 break;
122
123 case InternalNotificationEvent:
124 break;
125
126 case InternalSynchronizationTimer:
127 hdr->SignalState = FALSE;
128 break;
129
130 case InternalNotificationTimer:
131 break;
132
133 case InternalMutexType:
134 {
135 PKMUTEX Mutex;
136
137 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
138 hdr->SignalState--;
139 assert(hdr->SignalState <= 1);
140 if (hdr->SignalState == 0)
141 {
142 if (Thread == NULL)
143 {
144 DPRINT("Thread == NULL!\n");
145 KEBUGCHECK(0);
146 }
147 Abandoned = Mutex->Abandoned;
148 if (Thread != NULL)
149 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
150 Mutex->OwnerThread = Thread;
151 Mutex->Abandoned = FALSE;
152 }
153 }
154 break;
155
156 default:
157 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
158 KEBUGCHECK(0);
159 }
160
161 return Abandoned;
162 }
163
164 static BOOLEAN
165 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
166 PKTHREAD Thread)
167 {
168 if (hdr->Type == InternalMutexType)
169 {
170 PKMUTEX Mutex;
171
172 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
173
174 assert(hdr->SignalState <= 1);
175
176 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
177 {
178 return (TRUE);
179 }
180 else
181 {
182 return (FALSE);
183 }
184 }
185
186 if (hdr->SignalState <= 0)
187 {
188 return (FALSE);
189 }
190 else
191 {
192 return (TRUE);
193 }
194 }
195
196 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus, BOOL Unblock)
197 {
198 PKWAIT_BLOCK WaitBlock, PrevWaitBlock;
199 BOOLEAN WasWaiting = FALSE;
200 KIRQL OldIrql;
201
202 OldIrql = KeAcquireDispatcherDatabaseLock ();
203
204 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
205 if (WaitBlock != NULL)
206 {
207 WasWaiting = TRUE;
208 }
209 while (WaitBlock != NULL)
210 {
211 if (WaitBlock->WaitListEntry.Flink != NULL && WaitBlock->WaitListEntry.Blink != NULL)
212 {
213 RemoveEntryList (&WaitBlock->WaitListEntry);
214 WaitBlock->WaitListEntry.Flink = WaitBlock->WaitListEntry.Blink = NULL;
215 }
216 PrevWaitBlock = WaitBlock;
217 WaitBlock = WaitBlock->NextWaitBlock;
218 PrevWaitBlock->NextWaitBlock = NULL;
219 }
220 Thread->Tcb.WaitBlockList = NULL;
221
222 if (WasWaiting && Unblock)
223 {
224 PsUnblockThread(Thread, &WaitStatus);
225 }
226
227 KeReleaseDispatcherDatabaseLock (OldIrql);
228 }
229
230 static BOOLEAN
231 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
232 BOOLEAN WakeAll)
233 {
234 PKWAIT_BLOCK Waiter;
235 PKWAIT_BLOCK WaiterHead;
236 PLIST_ENTRY EnumEntry;
237 NTSTATUS Status;
238 BOOLEAN Abandoned;
239 BOOLEAN AllSignaled;
240 BOOLEAN WakedAny = FALSE;
241
242 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
243 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
244 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
245
246 if (IsListEmpty(&hdr->WaitListHead))
247 {
248 return (FALSE);
249 }
250
251 //enum waiters for this dispatcher object
252 EnumEntry = hdr->WaitListHead.Flink;
253 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
254 {
255 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
256 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
257 EnumEntry = EnumEntry->Flink;
258 assert(WaiterHead->Thread != NULL);
259 assert(WaiterHead->Thread->WaitBlockList != NULL);
260
261 Abandoned = FALSE;
262
263 if (WaiterHead->WaitType == WaitAny)
264 {
265 DPRINT("WaitAny: Remove all wait blocks.\n");
266 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
267 {
268 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
269 {
270 RemoveEntryList(&Waiter->WaitListEntry);
271 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
272 }
273 }
274
275 WaiterHead->Thread->WaitBlockList = NULL;
276
277 /*
278 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
279 * but thats ok since WakeAll objects has no sideeffects.
280 */
281 Abandoned = KiSideEffectsBeforeWake(hdr, WaiterHead->Thread) ? TRUE : Abandoned;
282 }
283 else
284 {
285 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
286
287 AllSignaled = TRUE;
288
289 //all WaitAll obj. for thread need to be signaled to satisfy a wake
290 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
291 {
292 //no need to check hdr since it has to be signaled
293 if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
294 {
295 if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
296 {
297 AllSignaled = FALSE;
298 break;
299 }
300 }
301 }
302
303 if (AllSignaled)
304 {
305 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
306 {
307 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
308 {
309 RemoveEntryList(&Waiter->WaitListEntry);
310 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
311 }
312
313 if (Waiter->WaitType == WaitAll)
314 {
315 Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
316 ? TRUE : Abandoned;
317 }
318
319 //no WaitAny objects can possibly be signaled since we are here
320 assert(!(Waiter->WaitType == WaitAny
321 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
322 }
323
324 WaiterHead->Thread->WaitBlockList = NULL;
325 }
326 }
327
328 if (WaiterHead->Thread->WaitBlockList == NULL)
329 {
330 Status = WaiterHead->WaitKey;
331 if (Abandoned)
332 {
333 DPRINT("Abandoned mutex among objects");
334 Status += STATUS_ABANDONED_WAIT_0;
335 }
336
337 WakedAny = TRUE;
338 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
339 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
340 }
341 }
342
343 return WakedAny;
344 }
345
346
347 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
348 /*
349 * FUNCTION: Wake threads waiting on a dispatcher object
350 * NOTE: The exact semantics of waking are dependant on the type of object
351 */
352 {
353 BOOL Ret;
354
355 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
356 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
357 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
358 DPRINT("hdr->Type %x\n",hdr->Type);
359 switch (hdr->Type)
360 {
361 case InternalNotificationEvent:
362 return(KeDispatcherObjectWakeAll(hdr));
363
364 case InternalNotificationTimer:
365 return(KeDispatcherObjectWakeAll(hdr));
366
367 case InternalSynchronizationEvent:
368 return(KeDispatcherObjectWakeOne(hdr));
369
370 case InternalSynchronizationTimer:
371 return(KeDispatcherObjectWakeOne(hdr));
372
373 case InternalQueueType:
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);
382 } while(hdr->SignalState > 0 && Ret) ;
383 return(Ret);
384 }
385 else return FALSE;
386
387 case InternalProcessType:
388 return(KeDispatcherObjectWakeAll(hdr));
389
390 case InternalThreadType:
391 return(KeDispatcherObjectWakeAll(hdr));
392
393 case InternalMutexType:
394 return(KeDispatcherObjectWakeOne(hdr));
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 == ExSemaphoreType ||
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 WaitIrql;
492 KIRQL OldIrql;
493 BOOLEAN Abandoned;
494
495 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
496 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
497
498 CurrentThread = KeGetCurrentThread();
499 WaitIrql = KeGetCurrentIrql();
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 > EX_MAXIMUM_WAIT_OBJECTS)
516 {
517 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
518 return (STATUS_UNSUCCESSFUL);
519 }
520 }
521
522 /*
523 * Set up the timeout if required
524 */
525 if (Timeout != NULL && Timeout->QuadPart != 0)
526 {
527 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
528 }
529
530 do
531 {
532 if (CurrentThread->WaitNext)
533 {
534 OldIrql = CurrentThread->WaitIrql;
535 CurrentThread->WaitNext = 0;
536 CurrentThread->WaitIrql = PASSIVE_LEVEL;
537 }
538 else
539 {
540 OldIrql = KeAcquireDispatcherDatabaseLock ();
541 }
542
543 /*
544 * If we are going to wait alertably and a user apc is pending
545 * then return
546 */
547 if (Alertable && KiTestAlert())
548 {
549 KeReleaseDispatcherDatabaseLock(OldIrql);
550 return (STATUS_USER_APC);
551 }
552
553 /*
554 * Check if the wait is (already) satisfied
555 */
556 CountSignaled = 0;
557 Abandoned = FALSE;
558 for (i = 0; i < Count; i++)
559 {
560 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
561
562 if (KiIsObjectSignalled(hdr, CurrentThread))
563 {
564 CountSignaled++;
565
566 if (WaitType == WaitAny)
567 {
568 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
569
570 if (Timeout != NULL && Timeout->QuadPart != 0)
571 {
572 KeCancelTimer(&CurrentThread->Timer);
573 }
574
575 KeReleaseDispatcherDatabaseLock(OldIrql);
576
577 DPRINT("One object is (already) signaled!\n");
578 if (Abandoned == TRUE)
579 {
580 return (STATUS_ABANDONED_WAIT_0 + i);
581 }
582
583 return (STATUS_WAIT_0 + i);
584 }
585 }
586 }
587
588 Abandoned = FALSE;
589 if ((WaitType == WaitAll) && (CountSignaled == Count))
590 {
591 for (i = 0; i < Count; i++)
592 {
593 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
594 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
595 }
596
597 if (Timeout != NULL && Timeout->QuadPart != 0)
598 {
599 KeCancelTimer(&CurrentThread->Timer);
600 }
601
602 KeReleaseDispatcherDatabaseLock(OldIrql);
603 DPRINT("All objects are (already) signaled!\n");
604
605 if (Abandoned == TRUE)
606 {
607 return (STATUS_ABANDONED_WAIT_0);
608 }
609
610 return (STATUS_WAIT_0);
611 }
612
613 //zero timeout is used for testing if the object(s) can be immediately acquired
614 if (Timeout != NULL && Timeout->QuadPart == 0)
615 {
616 KeReleaseDispatcherDatabaseLock(OldIrql);
617 return STATUS_TIMEOUT;
618 }
619
620 /*
621 * Check if we have already timed out
622 */
623 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
624 {
625 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
626 KeCancelTimer(&CurrentThread->Timer);
627 KeReleaseDispatcherDatabaseLock(OldIrql);
628 return (STATUS_TIMEOUT);
629 }
630
631 /* Append wait block to the KTHREAD wait block list */
632 CurrentThread->WaitBlockList = blk = WaitBlockArray;
633
634 /*
635 * Set up the wait
636 */
637 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
638
639 for (i = 0; i < Count; i++)
640 {
641 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
642
643 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
644 blk->Thread = CurrentThread;
645 blk->WaitKey = (USHORT)(STATUS_WAIT_0 + i);
646 blk->WaitType = (USHORT)WaitType;
647
648 if (i == (Count - 1))
649 {
650 if (Timeout != NULL)
651 {
652 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
653 }
654 else
655 {
656 blk->NextWaitBlock = NULL;
657 }
658 }
659 else
660 {
661 blk->NextWaitBlock = blk + 1;
662 }
663
664 /*
665 * add wait block to disp. obj. wait list
666 * Use FIFO for all waits except for queues which use LIFO
667 */
668 if (WaitReason == WrQueue)
669 {
670 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
671 }
672 else
673 {
674 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
675 }
676
677 blk = blk->NextWaitBlock;
678 }
679
680 if (Timeout != NULL)
681 {
682 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
683 CurrentThread->WaitBlock[3].Thread = CurrentThread;
684 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
685 CurrentThread->WaitBlock[3].WaitType = WaitAny;
686 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
687
688 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
689 &CurrentThread->WaitBlock[3].WaitListEntry);
690 }
691
692 //io completion
693 if (CurrentThread->Queue)
694 {
695 CurrentThread->Queue->CurrentCount--;
696 if (WaitReason != WrQueue && CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
697 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
698 {
699 KeDispatcherObjectWake(&CurrentThread->Queue->Header);
700 }
701 }
702
703 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, (UCHAR)WaitReason);
704
705 //io completion
706 if (CurrentThread->Queue)
707 {
708 CurrentThread->Queue->CurrentCount++;
709 }
710
711
712 }
713 while (Status == STATUS_KERNEL_APC);
714
715 if (Timeout != NULL)
716 {
717 KeCancelTimer(&CurrentThread->Timer);
718 }
719
720 DPRINT("Returning from KeWaitForMultipleObjects()\n");
721 return (Status);
722 }
723
724 VOID KeInitializeDispatcher(VOID)
725 {
726 KeInitializeSpinLock(&DispatcherDatabaseLock);
727 }
728
729 NTSTATUS STDCALL
730 NtWaitForMultipleObjects(IN ULONG Count,
731 IN HANDLE Object [],
732 IN WAIT_TYPE WaitType,
733 IN BOOLEAN Alertable,
734 IN PLARGE_INTEGER UnsafeTime)
735 {
736 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
737 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
738 NTSTATUS Status;
739 ULONG i, j;
740 KPROCESSOR_MODE WaitMode;
741 LARGE_INTEGER Time;
742
743 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
744 "Time %x)\n", Count,Object,Alertable,Time);
745
746 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
747 return STATUS_UNSUCCESSFUL;
748
749 if (UnsafeTime)
750 {
751 Status = MmCopyFromCaller(&Time, UnsafeTime, sizeof(LARGE_INTEGER));
752 if (!NT_SUCCESS(Status))
753 {
754 return(Status);
755 }
756 }
757
758 WaitMode = ExGetPreviousMode();
759
760 /* reference all objects */
761 for (i = 0; i < Count; i++)
762 {
763 Status = ObReferenceObjectByHandle(Object[i],
764 SYNCHRONIZE,
765 NULL,
766 WaitMode,
767 &ObjectPtrArray[i],
768 NULL);
769 if (!NT_SUCCESS(Status) || !KiIsObjectWaitable(ObjectPtrArray[i]))
770 {
771 if (NT_SUCCESS(Status))
772 {
773 DPRINT1("Waiting for object type '%wZ' is not supported\n",
774 &BODY_TO_HEADER(ObjectPtrArray[i])->ObjectType->TypeName);
775 Status = STATUS_HANDLE_NOT_WAITABLE;
776 i++;
777 }
778 /* dereference all referenced objects */
779 for (j = 0; j < i; j++)
780 {
781 ObDereferenceObject(ObjectPtrArray[j]);
782 }
783
784 return(Status);
785 }
786 }
787
788 Status = KeWaitForMultipleObjects(Count,
789 ObjectPtrArray,
790 WaitType,
791 UserRequest,
792 WaitMode,
793 Alertable,
794 UnsafeTime ? &Time : NULL,
795 WaitBlockArray);
796
797 /* dereference all objects */
798 for (i = 0; i < Count; i++)
799 {
800 ObDereferenceObject(ObjectPtrArray[i]);
801 }
802
803 return(Status);
804 }
805
806
807 /*
808 * @implemented
809 */
810 NTSTATUS STDCALL
811 NtWaitForSingleObject(IN HANDLE Object,
812 IN BOOLEAN Alertable,
813 IN PLARGE_INTEGER UnsafeTime)
814 {
815 PVOID ObjectPtr;
816 NTSTATUS Status;
817 KPROCESSOR_MODE WaitMode;
818 LARGE_INTEGER Time;
819
820 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
821 Object,Alertable,Time);
822
823 if (UnsafeTime)
824 {
825 Status = MmCopyFromCaller(&Time, UnsafeTime, sizeof(LARGE_INTEGER));
826 if (!NT_SUCCESS(Status))
827 {
828 return(Status);
829 }
830 }
831
832 WaitMode = ExGetPreviousMode();
833
834 Status = ObReferenceObjectByHandle(Object,
835 SYNCHRONIZE,
836 NULL,
837 WaitMode,
838 &ObjectPtr,
839 NULL);
840 if (!NT_SUCCESS(Status))
841 {
842 return(Status);
843 }
844 if (!KiIsObjectWaitable(ObjectPtr))
845 {
846 DPRINT1("Waiting for object type '%wZ' is not supported\n",
847 &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName);
848 Status = STATUS_HANDLE_NOT_WAITABLE;
849 }
850 else
851 {
852 Status = KeWaitForSingleObject(ObjectPtr,
853 UserRequest,
854 WaitMode,
855 Alertable,
856 UnsafeTime ? &Time : NULL);
857 }
858
859 ObDereferenceObject(ObjectPtr);
860
861 return(Status);
862 }
863
864
865 NTSTATUS STDCALL
866 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
867 IN HANDLE WaitObject,
868 IN BOOLEAN Alertable,
869 IN PLARGE_INTEGER Time)
870 {
871 KPROCESSOR_MODE WaitMode;
872 DISPATCHER_HEADER* hdr;
873 PVOID SignalObj;
874 PVOID WaitObj;
875 NTSTATUS Status;
876
877 WaitMode = ExGetPreviousMode();
878 Status = ObReferenceObjectByHandle(SignalObject,
879 0,
880 NULL,
881 WaitMode,
882 &SignalObj,
883 NULL);
884 if (!NT_SUCCESS(Status))
885 {
886 return Status;
887 }
888
889 Status = ObReferenceObjectByHandle(WaitObject,
890 SYNCHRONIZE,
891 NULL,
892 WaitMode,
893 &WaitObj,
894 NULL);
895 if (!NT_SUCCESS(Status))
896 {
897 ObDereferenceObject(SignalObj);
898 return Status;
899 }
900
901 hdr = (DISPATCHER_HEADER *)SignalObj;
902 switch (hdr->Type)
903 {
904 case InternalNotificationEvent:
905 case InternalSynchronizationEvent:
906 KeSetEvent(SignalObj,
907 EVENT_INCREMENT,
908 TRUE);
909 break;
910
911 case InternalMutexType:
912 KeReleaseMutex(SignalObj,
913 TRUE);
914 break;
915
916 case InternalSemaphoreType:
917 KeReleaseSemaphore(SignalObj,
918 SEMAPHORE_INCREMENT,
919 1,
920 TRUE);
921 break;
922
923 default:
924 ObDereferenceObject(SignalObj);
925 ObDereferenceObject(WaitObj);
926 return STATUS_OBJECT_TYPE_MISMATCH;
927 }
928
929 Status = KeWaitForSingleObject(WaitObj,
930 UserRequest,
931 WaitMode,
932 Alertable,
933 Time);
934
935 ObDereferenceObject(SignalObj);
936 ObDereferenceObject(WaitObj);
937
938 return Status;
939 }