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