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