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