Fixed prototype for MmSetAddressRangeModified().
[reactos.git] / 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 <ddk/ntddk.h>
20 #include <internal/ke.h>
21 #include <internal/ps.h>
22 #include <internal/ob.h>
23 #include <internal/id.h>
24 #include <ntos/ntdef.h>
25
26 #define NDEBUG
27 #include <internal/debug.h>
28
29 /* GLOBALS ******************************************************************/
30
31 static KSPIN_LOCK DispatcherDatabaseLock;
32 static BOOLEAN WaitSet = FALSE;
33 static KIRQL oldlvl = PASSIVE_LEVEL;
34 static PKTHREAD Owner = NULL;
35
36 #define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
37 #define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
38
39 /* FUNCTIONS *****************************************************************/
40
41 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
42                                   ULONG Type,
43                                   ULONG Size,
44                                   ULONG SignalState)
45 {
46    Header->Type = Type;
47    Header->Absolute = 0;
48    Header->Inserted = 0;
49    Header->Size = Size;
50    Header->SignalState = SignalState;
51    InitializeListHead(&(Header->WaitListHead));
52 }
53
54
55 VOID KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
56 /*
57  * PURPOSE: Acquires the dispatcher database lock for the caller
58  */
59 {
60    DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
61    if (WaitSet && Owner == KeGetCurrentThread())
62      {
63         return;
64      }
65    KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
66    WaitSet = Wait;
67    Owner = KeGetCurrentThread();
68 }
69
70 #ifndef LIBCAPTIVE
71
72 VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
73 {
74   DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x) WaitSet=%x\n", Wait, WaitSet);
75   if (Wait == WaitSet)
76     {
77       Owner = NULL;
78       KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
79     }
80 }
81
82 #endif /* LIBCAPTIVE */
83
84 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
85 {
86    DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x) WaitSet=%x\n",Wait,WaitSet);
87    if (Wait == WaitSet)
88      {
89         Owner = NULL;
90         KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
91      }
92 }
93
94 #ifndef LIBCAPTIVE
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
201    KeAcquireDispatcherDatabaseLock(FALSE);
202
203    WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
204    if (WaitBlock != NULL)
205      {
206         WasWaiting = TRUE;
207      }
208    while (WaitBlock != NULL)
209      {
210         if (WaitBlock->WaitListEntry.Flink != NULL && WaitBlock->WaitListEntry.Blink != NULL)
211           { 
212             RemoveEntryList (&WaitBlock->WaitListEntry);
213             WaitBlock->WaitListEntry.Flink = WaitBlock->WaitListEntry.Blink = NULL;
214           }
215         PrevWaitBlock = WaitBlock;
216         WaitBlock = WaitBlock->NextWaitBlock;
217         PrevWaitBlock->NextWaitBlock = NULL;
218      }
219    Thread->Tcb.WaitBlockList = NULL;
220
221    if (WasWaiting && Unblock)
222      {
223         PsUnblockThread(Thread, &WaitStatus);
224      }
225
226    KeReleaseDispatcherDatabaseLock(FALSE);
227 }
228
229 static BOOLEAN
230 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
231                                BOOLEAN WakeAll)
232 {
233    PKWAIT_BLOCK Waiter;
234    PKWAIT_BLOCK WaiterHead;
235    PLIST_ENTRY EnumEntry;
236    NTSTATUS Status;
237    BOOLEAN Abandoned;
238    BOOLEAN AllSignaled;
239    BOOLEAN WakedAny = FALSE;
240
241    DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
242    DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
243            hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
244
245    if (IsListEmpty(&hdr->WaitListHead))
246    {
247       return (FALSE);
248    }
249
250    //enum waiters for this dispatcher object
251    EnumEntry = hdr->WaitListHead.Flink;
252    while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
253    {
254       WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
255       DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
256       EnumEntry = EnumEntry->Flink;
257       assert(WaiterHead->Thread != NULL);
258       assert(WaiterHead->Thread->WaitBlockList != NULL);
259
260       Abandoned = FALSE;
261
262       if (WaiterHead->WaitType == WaitAny)
263       {
264          DPRINT("WaitAny: Remove all wait blocks.\n");
265          for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
266          {
267             if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
268               {
269                 RemoveEntryList(&Waiter->WaitListEntry);
270                 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
271               }
272          }
273
274          WaiterHead->Thread->WaitBlockList = NULL;
275
276          /*
277           * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
278           * but thats ok since WakeAll objects has no sideeffects.
279           */
280          Abandoned = KiSideEffectsBeforeWake(hdr, WaiterHead->Thread) ? TRUE : Abandoned;
281       }
282       else
283       {
284          DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
285
286          AllSignaled = TRUE;
287
288          //all WaitAll obj. for thread need to be signaled to satisfy a wake
289          for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
290          {
291             //no need to check hdr since it has to be signaled
292             if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
293             {
294                if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
295                {
296                   AllSignaled = FALSE;
297                   break;
298                }
299             }
300          }
301
302          if (AllSignaled)
303          {
304             for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
305             {
306                if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
307                {
308                   RemoveEntryList(&Waiter->WaitListEntry);
309                   Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
310                }
311          
312                if (Waiter->WaitType == WaitAll)
313                {
314                   Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
315                      ? TRUE : Abandoned;
316                }
317
318                //no WaitAny objects can possibly be signaled since we are here
319                assert(!(Waiter->WaitType == WaitAny
320                                           && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
321             }
322
323             WaiterHead->Thread->WaitBlockList = NULL;
324          }
325       }
326
327       if (WaiterHead->Thread->WaitBlockList == NULL)
328       {
329          Status = WaiterHead->WaitKey;
330          if (Abandoned)
331          {
332             DPRINT("Abandoned mutex among objects");
333             Status += STATUS_ABANDONED_WAIT_0;
334          }
335
336          WakedAny = TRUE;
337          DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
338          PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
339       }
340    }
341
342    return WakedAny;
343 }
344
345
346 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
347 /*
348  * FUNCTION: Wake threads waiting on a dispatcher object
349  * NOTE: The exact semantics of waking are dependant on the type of object
350  */
351 {
352    BOOL Ret;
353
354    DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
355 //   DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
356 //        &hdr->WaitListHead,hdr->WaitListHead.Flink);
357    DPRINT("hdr->Type %x\n",hdr->Type);
358    switch (hdr->Type)
359      {
360       case InternalNotificationEvent:
361         return(KeDispatcherObjectWakeAll(hdr));
362
363       case InternalNotificationTimer:
364         return(KeDispatcherObjectWakeAll(hdr));
365
366       case InternalSynchronizationEvent:
367         return(KeDispatcherObjectWakeOne(hdr));
368
369       case InternalSynchronizationTimer:
370         return(KeDispatcherObjectWakeOne(hdr));
371
372       case InternalQueueType:
373       case InternalSemaphoreType:
374         DPRINT("hdr->SignalState %d\n", hdr->SignalState);
375         if(hdr->SignalState>0)
376           {
377             do
378               {
379                 DPRINT("Waking one semaphore waiter\n");
380                 Ret = KeDispatcherObjectWakeOne(hdr);
381               } while(hdr->SignalState > 0 &&  Ret) ;
382             return(Ret);
383           }
384         else return FALSE;
385
386      case InternalProcessType:
387         return(KeDispatcherObjectWakeAll(hdr));
388
389      case InternalThreadType:
390        return(KeDispatcherObjectWakeAll(hdr));
391
392      case InternalMutexType:
393        return(KeDispatcherObjectWakeOne(hdr));
394      }
395    DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
396    KEBUGCHECK(0);
397    return(FALSE);
398 }
399
400
401 /*
402  * @implemented
403  */
404 NTSTATUS STDCALL
405 KeWaitForSingleObject(PVOID Object,
406                       KWAIT_REASON WaitReason,
407                       KPROCESSOR_MODE WaitMode,
408                       BOOLEAN Alertable,
409                       PLARGE_INTEGER Timeout)
410 /*
411  * FUNCTION: Puts the current thread into a wait state until the
412  * given dispatcher object is set to signalled
413  * ARGUMENTS:
414  *         Object = Object to wait on
415  *         WaitReason = Reason for the wait (debugging aid)
416  *         WaitMode = Can be KernelMode or UserMode, if UserMode then
417  *                    user-mode APCs can be delivered and the thread's
418  *                    stack can be paged out
419  *         Altertable = Specifies if the wait is a alertable
420  *         Timeout = Optional timeout value
421  * RETURNS: Status
422  */
423 {
424    return KeWaitForMultipleObjects(1,
425                                    &Object,
426                                    WaitAny,
427                                    WaitReason,
428                                    WaitMode,
429                                    Alertable,
430                                    Timeout,
431                                    NULL);
432 }
433
434
435 inline
436 PVOID
437 KiGetWaitableObjectFromObject(PVOID Object)
438 {
439    //special case when waiting on file objects
440    if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
441    {
442       return &((PFILE_OBJECT)Object)->Event;
443    }
444
445    return Object;
446 }
447
448
449 /*
450  * @implemented
451  */
452 NTSTATUS STDCALL
453 KeWaitForMultipleObjects(ULONG Count,
454                          PVOID Object[],
455                          WAIT_TYPE WaitType,
456                          KWAIT_REASON WaitReason,
457                          KPROCESSOR_MODE WaitMode,
458                          BOOLEAN Alertable,
459                          PLARGE_INTEGER Timeout,
460                          PKWAIT_BLOCK WaitBlockArray)
461 {
462    DISPATCHER_HEADER *hdr;
463    PKWAIT_BLOCK blk;
464    PKTHREAD CurrentThread;
465    ULONG CountSignaled;
466    ULONG i;
467    NTSTATUS Status;
468    KIRQL WaitIrql;
469    BOOLEAN Abandoned;
470
471    DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
472           "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
473
474    CurrentThread = KeGetCurrentThread();
475    WaitIrql = KeGetCurrentIrql();
476
477    /*
478     * Work out where we are going to put the wait blocks
479     */
480    if (WaitBlockArray == NULL)
481    {
482       if (Count > THREAD_WAIT_OBJECTS)
483       {
484          DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
485          return (STATUS_UNSUCCESSFUL);
486       }
487       WaitBlockArray = &CurrentThread->WaitBlock[0];
488    }
489    else
490    {
491       if (Count > EX_MAXIMUM_WAIT_OBJECTS)
492       {
493          DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
494          return (STATUS_UNSUCCESSFUL);
495       }
496    }
497
498    /*
499     * Set up the timeout if required
500     */
501    if (Timeout != NULL && Timeout->QuadPart != 0)
502    {
503       KeInitializeTimer(&CurrentThread->Timer);
504       KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
505    }
506
507    do
508    {
509       KeAcquireDispatcherDatabaseLock(FALSE);
510
511       /*
512        * If we are going to wait alertably and a user apc is pending
513        * then return
514        */
515       if (Alertable && KiTestAlert())
516       {
517          KeReleaseDispatcherDatabaseLock(FALSE);
518          return (STATUS_USER_APC);
519       }
520
521       /*
522        * Check if the wait is (already) satisfied
523        */
524       CountSignaled = 0;
525       Abandoned = FALSE;
526       for (i = 0; i < Count; i++)
527       {
528          hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
529
530          if (KiIsObjectSignalled(hdr, CurrentThread))
531          {
532             CountSignaled++;
533
534             if (WaitType == WaitAny)
535             {
536                Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
537
538                if (Timeout != NULL && Timeout->QuadPart != 0)
539                {
540                   KeCancelTimer(&CurrentThread->Timer);
541                }
542
543                KeReleaseDispatcherDatabaseLock(FALSE);
544
545                DPRINT("One object is (already) signaled!\n");
546                if (Abandoned == TRUE)
547                {
548                   return (STATUS_ABANDONED_WAIT_0 + i);
549                }
550
551                return (STATUS_WAIT_0 + i);
552             }
553          }
554       }
555
556       Abandoned = FALSE;
557       if ((WaitType == WaitAll) && (CountSignaled == Count))
558       {
559          for (i = 0; i < Count; i++)
560          {
561             hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
562             Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
563          }
564
565          if (Timeout != NULL && Timeout->QuadPart != 0)
566          {
567             KeCancelTimer(&CurrentThread->Timer);
568          }
569
570          KeReleaseDispatcherDatabaseLock(FALSE);
571          DPRINT("All objects are (already) signaled!\n");
572
573          if (Abandoned == TRUE)
574          {
575             return (STATUS_ABANDONED_WAIT_0);
576          }
577
578          return (STATUS_WAIT_0);
579       }
580
581       //zero timeout is used for testing if the object(s) can be immediately acquired
582       if (Timeout != NULL && Timeout->QuadPart == 0)
583       {
584          KeReleaseDispatcherDatabaseLock(FALSE);
585          return STATUS_TIMEOUT;
586       }
587
588       /*
589        * Check if we have already timed out
590        */
591       if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
592       {
593          KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
594          KeCancelTimer(&CurrentThread->Timer);
595          KeReleaseDispatcherDatabaseLock(FALSE);
596          return (STATUS_TIMEOUT);
597       }
598
599       /* Append wait block to the KTHREAD wait block list */
600       CurrentThread->WaitBlockList = blk = WaitBlockArray;
601
602       /*
603        * Set up the wait
604        */
605       CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
606
607       for (i = 0; i < Count; i++)
608       {
609          hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
610
611          blk->Object = KiGetWaitableObjectFromObject(Object[i]);
612          blk->Thread = CurrentThread;
613          blk->WaitKey = STATUS_WAIT_0 + i;
614          blk->WaitType = WaitType;
615
616          if (i == (Count - 1))
617          {
618             if (Timeout != NULL)
619             {
620                blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
621             }
622             else
623             {
624                blk->NextWaitBlock = NULL;
625             }
626          }
627          else
628          {
629             blk->NextWaitBlock = blk + 1;
630          }
631
632          /*
633           * add wait block to disp. obj. wait list
634           * Use FIFO for all waits except for queues which use LIFO
635           */
636          if (WaitReason == WrQueue)
637          {
638             InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
639          }
640          else
641          {
642             InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
643          }
644
645          blk = blk->NextWaitBlock;
646       }
647
648       if (Timeout != NULL)
649       {
650          CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
651          CurrentThread->WaitBlock[3].Thread = CurrentThread;
652          CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
653          CurrentThread->WaitBlock[3].WaitType = WaitAny;
654          CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
655
656          InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
657                         &CurrentThread->WaitBlock[3].WaitListEntry);
658       }
659
660       //io completion
661       if (CurrentThread->Queue)
662       {
663          CurrentThread->Queue->CurrentCount--;   
664          if (WaitReason != WrQueue && CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
665              !IsListEmpty(&CurrentThread->Queue->EntryListHead))
666          {
667             KeDispatcherObjectWake(&CurrentThread->Queue->Header);
668          }
669       }
670
671       PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
672
673       //io completion
674       if (CurrentThread->Queue)
675       {
676          CurrentThread->Queue->CurrentCount++;
677       }
678
679
680    }
681    while (Status == STATUS_KERNEL_APC);
682
683    if (Timeout != NULL)
684    {
685       KeCancelTimer(&CurrentThread->Timer);
686    }
687
688    DPRINT("Returning from KeWaitForMultipleObjects()\n");
689    return (Status);
690 }
691
692 #endif /* LIBCAPTIVE */
693
694 VOID KeInitializeDispatcher(VOID)
695 {
696    KeInitializeSpinLock(&DispatcherDatabaseLock);
697 }
698
699 #ifndef LIBCAPTIVE
700
701 NTSTATUS STDCALL
702 NtWaitForMultipleObjects(IN ULONG Count,
703                          IN HANDLE Object [],
704                          IN WAIT_TYPE WaitType,
705                          IN BOOLEAN Alertable,
706                          IN PLARGE_INTEGER Time)
707 {
708    KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
709    PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
710    NTSTATUS Status;
711    ULONG i, j;
712    KPROCESSOR_MODE WaitMode;
713
714    DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
715           "Time %x)\n", Count,Object,Alertable,Time);
716
717    if (Count > EX_MAXIMUM_WAIT_OBJECTS)
718      return STATUS_UNSUCCESSFUL;
719
720    WaitMode = ExGetPreviousMode();
721
722    /* reference all objects */
723    for (i = 0; i < Count; i++)
724      {
725         Status = ObReferenceObjectByHandle(Object[i],
726                                            SYNCHRONIZE,
727                                            NULL,
728                                            WaitMode,
729                                            &ObjectPtrArray[i],
730                                            NULL);
731         if (Status != STATUS_SUCCESS)
732           {
733              /* dereference all referenced objects */
734              for (j = 0; j < i; j++)
735                {
736                   ObDereferenceObject(ObjectPtrArray[j]);
737                }
738
739              return(Status);
740           }
741      }
742
743    Status = KeWaitForMultipleObjects(Count,
744                                      ObjectPtrArray,
745                                      WaitType,
746                                      UserRequest,
747                                      WaitMode,
748                                      Alertable,
749                                      Time,
750                                      WaitBlockArray);
751
752    /* dereference all objects */
753    for (i = 0; i < Count; i++)
754      {
755         ObDereferenceObject(ObjectPtrArray[i]);
756      }
757
758    return(Status);
759 }
760
761
762 /*
763  * @implemented
764  */
765 NTSTATUS STDCALL
766 NtWaitForSingleObject(IN HANDLE Object,
767                       IN BOOLEAN Alertable,
768                       IN PLARGE_INTEGER Time)
769 {
770    PVOID ObjectPtr;
771    NTSTATUS Status;
772    KPROCESSOR_MODE WaitMode;
773
774    DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
775           Object,Alertable,Time);
776
777    WaitMode = ExGetPreviousMode();
778
779    Status = ObReferenceObjectByHandle(Object,
780                                       SYNCHRONIZE,
781                                       NULL,
782                                       WaitMode,
783                                       &ObjectPtr,
784                                       NULL);
785    if (!NT_SUCCESS(Status))
786      {
787         return(Status);
788      }
789
790    Status = KeWaitForSingleObject(ObjectPtr,
791                                   UserRequest,
792                                   WaitMode,
793                                   Alertable,
794                                   Time);
795
796    ObDereferenceObject(ObjectPtr);
797
798    return(Status);
799 }
800
801
802 NTSTATUS STDCALL
803 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
804                                IN HANDLE WaitObject,
805                                IN BOOLEAN Alertable,
806                                IN PLARGE_INTEGER Time)
807 {
808    KPROCESSOR_MODE WaitMode;
809    DISPATCHER_HEADER* hdr;
810    PVOID SignalObj;
811    PVOID WaitObj;
812    NTSTATUS Status;
813
814    WaitMode = ExGetPreviousMode();
815    Status = ObReferenceObjectByHandle(SignalObject,
816                                       0,
817                                       NULL,
818                                       WaitMode,
819                                       &SignalObj,
820                                       NULL);
821    if (!NT_SUCCESS(Status))
822      {
823         return Status;
824      }
825
826    Status = ObReferenceObjectByHandle(WaitObject,
827                                       SYNCHRONIZE,
828                                       NULL,
829                                       WaitMode,
830                                       &WaitObj,
831                                       NULL);
832    if (!NT_SUCCESS(Status))
833      {
834         ObDereferenceObject(SignalObj);
835         return Status;
836      }
837
838    hdr = (DISPATCHER_HEADER *)SignalObj;
839    switch (hdr->Type)
840      {
841       case InternalNotificationEvent:
842       case InternalSynchronizationEvent:
843         KeSetEvent(SignalObj,
844                    EVENT_INCREMENT,
845                    TRUE);
846         break;
847
848       case InternalMutexType:
849         KeReleaseMutex(SignalObj,
850                        TRUE);
851         break;
852
853       case InternalSemaphoreType:
854         KeReleaseSemaphore(SignalObj,
855                            SEMAPHORE_INCREMENT,
856                            1,
857                            TRUE);
858         break;
859
860       default:
861         ObDereferenceObject(SignalObj);
862         ObDereferenceObject(WaitObj);
863         return STATUS_OBJECT_TYPE_MISMATCH;
864      }
865
866    Status = KeWaitForSingleObject(WaitObj,
867                                   UserRequest,
868                                   WaitMode,
869                                   Alertable,
870                                   Time);
871
872    ObDereferenceObject(SignalObj);
873    ObDereferenceObject(WaitObj);
874
875    return Status;
876 }
877
878 #endif /* LIBCAPTIVE */