Use upper-case ASSERT macros.
[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 ASSERT(0 < Count && Count <= EX_MAXIMUM_WAIT_OBJECTS);
499
500 CurrentThread = KeGetCurrentThread();
501 WaitIrql = KeGetCurrentIrql();
502
503 /*
504 * Work out where we are going to put the wait blocks
505 */
506 if (WaitBlockArray == NULL)
507 {
508 if (Count > THREAD_WAIT_OBJECTS)
509 {
510 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
511 return (STATUS_UNSUCCESSFUL);
512 }
513 WaitBlockArray = &CurrentThread->WaitBlock[0];
514 }
515 else
516 {
517 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
518 {
519 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
520 return (STATUS_UNSUCCESSFUL);
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 OldIrql = CurrentThread->WaitIrql;
537 CurrentThread->WaitNext = 0;
538 CurrentThread->WaitIrql = PASSIVE_LEVEL;
539 }
540 else
541 {
542 OldIrql = KeAcquireDispatcherDatabaseLock ();
543 }
544
545 /*
546 * If we are going to wait alertably and a user apc is pending
547 * then return
548 */
549 if (Alertable && KiTestAlert())
550 {
551 KeReleaseDispatcherDatabaseLock(OldIrql);
552 return (STATUS_USER_APC);
553 }
554
555 /*
556 * Check if the wait is (already) satisfied
557 */
558 CountSignaled = 0;
559 Abandoned = FALSE;
560 for (i = 0; i < Count; i++)
561 {
562 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
563
564 if (KiIsObjectSignalled(hdr, CurrentThread))
565 {
566 CountSignaled++;
567
568 if (WaitType == WaitAny)
569 {
570 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
571
572 if (Timeout != NULL && Timeout->QuadPart != 0)
573 {
574 KeCancelTimer(&CurrentThread->Timer);
575 }
576
577 KeReleaseDispatcherDatabaseLock(OldIrql);
578
579 DPRINT("One object is (already) signaled!\n");
580 if (Abandoned == TRUE)
581 {
582 return (STATUS_ABANDONED_WAIT_0 + i);
583 }
584
585 return (STATUS_WAIT_0 + i);
586 }
587 }
588 }
589
590 Abandoned = FALSE;
591 if ((WaitType == WaitAll) && (CountSignaled == Count))
592 {
593 for (i = 0; i < Count; i++)
594 {
595 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
596 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
597 }
598
599 if (Timeout != NULL && Timeout->QuadPart != 0)
600 {
601 KeCancelTimer(&CurrentThread->Timer);
602 }
603
604 KeReleaseDispatcherDatabaseLock(OldIrql);
605 DPRINT("All objects are (already) signaled!\n");
606
607 if (Abandoned == TRUE)
608 {
609 return (STATUS_ABANDONED_WAIT_0);
610 }
611
612 return (STATUS_WAIT_0);
613 }
614
615 //zero timeout is used for testing if the object(s) can be immediately acquired
616 if (Timeout != NULL && Timeout->QuadPart == 0)
617 {
618 KeReleaseDispatcherDatabaseLock(OldIrql);
619 return STATUS_TIMEOUT;
620 }
621
622 /*
623 * Check if we have already timed out
624 */
625 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
626 {
627 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
628 KeCancelTimer(&CurrentThread->Timer);
629 KeReleaseDispatcherDatabaseLock(OldIrql);
630 return (STATUS_TIMEOUT);
631 }
632
633 /* Append wait block to the KTHREAD wait block list */
634 CurrentThread->WaitBlockList = blk = WaitBlockArray;
635
636 /*
637 * Set up the wait
638 */
639 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
640
641 for (i = 0; i < Count; i++)
642 {
643 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
644
645 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
646 blk->Thread = CurrentThread;
647 blk->WaitKey = (USHORT)(STATUS_WAIT_0 + i);
648 blk->WaitType = (USHORT)WaitType;
649
650 if (i == (Count - 1))
651 {
652 if (Timeout != NULL)
653 {
654 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
655 }
656 else
657 {
658 blk->NextWaitBlock = NULL;
659 }
660 }
661 else
662 {
663 blk->NextWaitBlock = blk + 1;
664 }
665
666 /*
667 * add wait block to disp. obj. wait list
668 * Use FIFO for all waits except for queues which use LIFO
669 */
670 if (WaitReason == WrQueue)
671 {
672 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
673 }
674 else
675 {
676 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
677 }
678
679 blk = blk->NextWaitBlock;
680 }
681
682 if (Timeout != NULL)
683 {
684 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
685 CurrentThread->WaitBlock[3].Thread = CurrentThread;
686 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
687 CurrentThread->WaitBlock[3].WaitType = WaitAny;
688 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
689
690 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
691 &CurrentThread->WaitBlock[3].WaitListEntry);
692 }
693
694 //io completion
695 if (CurrentThread->Queue)
696 {
697 CurrentThread->Queue->CurrentCount--;
698 if (WaitReason != WrQueue && CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
699 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
700 {
701 KeDispatcherObjectWake(&CurrentThread->Queue->Header);
702 }
703 }
704
705 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, (UCHAR)WaitReason);
706
707 //io completion
708 if (CurrentThread->Queue)
709 {
710 CurrentThread->Queue->CurrentCount++;
711 }
712
713
714 }
715 while (Status == STATUS_KERNEL_APC);
716
717 if (Timeout != NULL)
718 {
719 KeCancelTimer(&CurrentThread->Timer);
720 }
721
722 DPRINT("Returning from KeWaitForMultipleObjects()\n");
723 return (Status);
724 }
725
726 VOID KeInitializeDispatcher(VOID)
727 {
728 KeInitializeSpinLock(&DispatcherDatabaseLock);
729 }
730
731 NTSTATUS STDCALL
732 NtWaitForMultipleObjects(IN ULONG Count,
733 IN HANDLE Object [],
734 IN WAIT_TYPE WaitType,
735 IN BOOLEAN Alertable,
736 IN PLARGE_INTEGER UnsafeTime)
737 {
738 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
739 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
740 NTSTATUS Status;
741 ULONG i, j;
742 KPROCESSOR_MODE WaitMode;
743 LARGE_INTEGER Time;
744
745 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
746 "Time %x)\n", Count,Object,Alertable,Time);
747
748 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
749 return STATUS_UNSUCCESSFUL;
750 if (0 == Count)
751 return STATUS_INVALID_PARAMETER;
752
753 if (UnsafeTime)
754 {
755 Status = MmCopyFromCaller(&Time, UnsafeTime, sizeof(LARGE_INTEGER));
756 if (!NT_SUCCESS(Status))
757 {
758 return(Status);
759 }
760 }
761
762 WaitMode = ExGetPreviousMode();
763
764 /* reference all objects */
765 for (i = 0; i < Count; i++)
766 {
767 Status = ObReferenceObjectByHandle(Object[i],
768 SYNCHRONIZE,
769 NULL,
770 WaitMode,
771 &ObjectPtrArray[i],
772 NULL);
773 if (!NT_SUCCESS(Status) || !KiIsObjectWaitable(ObjectPtrArray[i]))
774 {
775 if (NT_SUCCESS(Status))
776 {
777 DPRINT1("Waiting for object type '%wZ' is not supported\n",
778 &BODY_TO_HEADER(ObjectPtrArray[i])->ObjectType->TypeName);
779 Status = STATUS_HANDLE_NOT_WAITABLE;
780 i++;
781 }
782 /* dereference all referenced objects */
783 for (j = 0; j < i; j++)
784 {
785 ObDereferenceObject(ObjectPtrArray[j]);
786 }
787
788 return(Status);
789 }
790 }
791
792 Status = KeWaitForMultipleObjects(Count,
793 ObjectPtrArray,
794 WaitType,
795 UserRequest,
796 WaitMode,
797 Alertable,
798 UnsafeTime ? &Time : NULL,
799 WaitBlockArray);
800
801 /* dereference all objects */
802 for (i = 0; i < Count; i++)
803 {
804 ObDereferenceObject(ObjectPtrArray[i]);
805 }
806
807 return(Status);
808 }
809
810
811 /*
812 * @implemented
813 */
814 NTSTATUS STDCALL
815 NtWaitForSingleObject(IN HANDLE Object,
816 IN BOOLEAN Alertable,
817 IN PLARGE_INTEGER UnsafeTime)
818 {
819 PVOID ObjectPtr;
820 NTSTATUS Status;
821 KPROCESSOR_MODE WaitMode;
822 LARGE_INTEGER Time;
823
824 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
825 Object,Alertable,Time);
826
827 if (UnsafeTime)
828 {
829 Status = MmCopyFromCaller(&Time, UnsafeTime, sizeof(LARGE_INTEGER));
830 if (!NT_SUCCESS(Status))
831 {
832 return(Status);
833 }
834 }
835
836 WaitMode = ExGetPreviousMode();
837
838 Status = ObReferenceObjectByHandle(Object,
839 SYNCHRONIZE,
840 NULL,
841 WaitMode,
842 &ObjectPtr,
843 NULL);
844 if (!NT_SUCCESS(Status))
845 {
846 return(Status);
847 }
848 if (!KiIsObjectWaitable(ObjectPtr))
849 {
850 DPRINT1("Waiting for object type '%wZ' is not supported\n",
851 &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName);
852 Status = STATUS_HANDLE_NOT_WAITABLE;
853 }
854 else
855 {
856 Status = KeWaitForSingleObject(ObjectPtr,
857 UserRequest,
858 WaitMode,
859 Alertable,
860 UnsafeTime ? &Time : NULL);
861 }
862
863 ObDereferenceObject(ObjectPtr);
864
865 return(Status);
866 }
867
868
869 NTSTATUS STDCALL
870 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
871 IN HANDLE WaitObject,
872 IN BOOLEAN Alertable,
873 IN PLARGE_INTEGER Time)
874 {
875 KPROCESSOR_MODE WaitMode;
876 DISPATCHER_HEADER* hdr;
877 PVOID SignalObj;
878 PVOID WaitObj;
879 NTSTATUS Status;
880
881 WaitMode = ExGetPreviousMode();
882 Status = ObReferenceObjectByHandle(SignalObject,
883 0,
884 NULL,
885 WaitMode,
886 &SignalObj,
887 NULL);
888 if (!NT_SUCCESS(Status))
889 {
890 return Status;
891 }
892
893 Status = ObReferenceObjectByHandle(WaitObject,
894 SYNCHRONIZE,
895 NULL,
896 WaitMode,
897 &WaitObj,
898 NULL);
899 if (!NT_SUCCESS(Status))
900 {
901 ObDereferenceObject(SignalObj);
902 return Status;
903 }
904
905 hdr = (DISPATCHER_HEADER *)SignalObj;
906 switch (hdr->Type)
907 {
908 case InternalNotificationEvent:
909 case InternalSynchronizationEvent:
910 KeSetEvent(SignalObj,
911 EVENT_INCREMENT,
912 TRUE);
913 break;
914
915 case InternalMutexType:
916 KeReleaseMutex(SignalObj,
917 TRUE);
918 break;
919
920 case InternalSemaphoreType:
921 KeReleaseSemaphore(SignalObj,
922 SEMAPHORE_INCREMENT,
923 1,
924 TRUE);
925 break;
926
927 default:
928 ObDereferenceObject(SignalObj);
929 ObDereferenceObject(WaitObj);
930 return STATUS_OBJECT_TYPE_MISMATCH;
931 }
932
933 Status = KeWaitForSingleObject(WaitObj,
934 UserRequest,
935 WaitMode,
936 Alertable,
937 Time);
938
939 ObDereferenceObject(SignalObj);
940 ObDereferenceObject(WaitObj);
941
942 return Status;
943 }