branch update for HEAD-2003021201
[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               current->NextWaitBlock = NULL;
286               /* if the last block is the timeout block then remove this block */
287               PrevBlock = current->Thread->WaitBlockList;
288               if (PrevBlock == &current->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
289                 {
290                   RemoveEntryList(&current->Thread->WaitBlock[3].WaitListEntry);
291                   current->Thread->WaitBlockList = NULL;
292                 }
293             }
294         }
295       KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
296       Status = current->WaitKey;
297       if (Abandoned)
298         Status += STATUS_ABANDONED_WAIT_0;
299       if (current->Thread->WaitBlockList == NULL)
300         {
301           PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
302                           &Status);
303         }
304     }
305   return(TRUE);
306 }
307
308 static BOOLEAN KeDispatcherObjectWakeOne(DISPATCHER_HEADER* hdr)
309 {
310   PKWAIT_BLOCK current;
311   PLIST_ENTRY current_entry;
312   PKWAIT_BLOCK PrevBlock;
313   NTSTATUS Status;
314   BOOLEAN Abandoned;
315
316   DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr);
317   DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
318          hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
319   if (IsListEmpty(&(hdr->WaitListHead)))
320     {
321       return(FALSE);
322     }
323   current_entry = RemoveHeadList(&(hdr->WaitListHead));
324   current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
325                               WaitListEntry);
326   DPRINT("current_entry %x current %x\n",current_entry,current);
327
328    if (current->WaitType == WaitAny)
329      {
330         DPRINT("WaitAny: Remove all wait blocks.\n");
331         for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
332           if( PrevBlock != current )
333             RemoveEntryList( &(PrevBlock->WaitListEntry) );
334         current->Thread->WaitBlockList = 0;
335      }
336    else
337      {
338         DPRINT("WaitAll: Remove the current wait block only.\n");
339
340         PrevBlock = current->Thread->WaitBlockList;
341         if (PrevBlock == current)
342            {
343               DPRINT( "WaitAll: Current block is list head.\n" );
344               current->Thread->WaitBlockList = current->NextWaitBlock;
345            }
346         else
347            {
348               DPRINT( "WaitAll: Current block is not list head.\n" );
349               while ( PrevBlock && PrevBlock->NextWaitBlock != current)
350                 {
351                    PrevBlock = PrevBlock->NextWaitBlock;
352                 }
353               if (PrevBlock)
354                 {
355                    PrevBlock->NextWaitBlock = current->NextWaitBlock;
356                 }
357            }
358         current->NextWaitBlock = NULL;
359         /* if the last block is the timeout block then remove this block */
360         PrevBlock = current->Thread->WaitBlockList;
361         if (PrevBlock == &current->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
362            {
363                RemoveEntryList(&current->Thread->WaitBlock[3].WaitListEntry);
364                current->Thread->WaitBlockList = NULL;
365            }
366     }
367
368   DPRINT("Waking %x\n",current->Thread);
369   KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
370   Status = current->WaitKey;
371   if (Abandoned)
372     Status += STATUS_ABANDONED_WAIT_0;
373       if (current->Thread->WaitBlockList == NULL)
374         {
375           PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
376                           &Status);
377         }
378   return(TRUE);
379 }
380
381 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
382 /*
383  * FUNCTION: Wake threads waiting on a dispatcher object
384  * NOTE: The exact semantics of waking are dependant on the type of object
385  */
386 {
387    BOOL Ret;
388
389    DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
390 //   DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
391 //        &hdr->WaitListHead,hdr->WaitListHead.Flink);
392    DPRINT("hdr->Type %x\n",hdr->Type);
393    switch (hdr->Type)
394      {
395       case InternalNotificationEvent:
396         return(KeDispatcherObjectWakeAll(hdr));
397         
398       case InternalNotificationTimer:
399         return(KeDispatcherObjectWakeAll(hdr));
400         
401       case InternalSynchronizationEvent:
402         return(KeDispatcherObjectWakeOne(hdr));
403
404       case InternalSynchronizationTimer:
405         return(KeDispatcherObjectWakeOne(hdr));
406         
407       case InternalSemaphoreType:
408         DPRINT("hdr->SignalState %d\n", hdr->SignalState);
409         if(hdr->SignalState>0)
410           {
411             do
412               {
413                 DPRINT("Waking one semaphore waiter\n");
414                 Ret = KeDispatcherObjectWakeOne(hdr);
415               } while(hdr->SignalState > 0 &&  Ret) ;
416             return(Ret);
417           }
418         else return FALSE;
419         
420      case InternalProcessType:
421         return(KeDispatcherObjectWakeAll(hdr));
422
423      case InternalThreadType:
424        return(KeDispatcherObjectWakeAll(hdr));
425         
426      case InternalMutexType:
427        return(KeDispatcherObjectWakeOne(hdr));
428      }
429    DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
430    KeBugCheck(0);
431    return(FALSE);
432 }
433
434
435 NTSTATUS STDCALL
436 KeWaitForSingleObject(PVOID Object,
437                       KWAIT_REASON WaitReason,
438                       KPROCESSOR_MODE WaitMode,
439                       BOOLEAN Alertable,
440                       PLARGE_INTEGER Timeout)
441 /*
442  * FUNCTION: Puts the current thread into a wait state until the
443  * given dispatcher object is set to signalled 
444  * ARGUMENTS:
445  *         Object = Object to wait on
446  *         WaitReason = Reason for the wait (debugging aid)
447  *         WaitMode = Can be KernelMode or UserMode, if UserMode then
448  *                    user-mode APCs can be delivered and the thread's
449  *                    stack can be paged out
450  *         Altertable = Specifies if the wait is a alertable
451  *         Timeout = Optional timeout value
452  * RETURNS: Status
453  */
454 {
455    DISPATCHER_HEADER* hdr = (DISPATCHER_HEADER *)Object;
456    PKTHREAD CurrentThread;
457    NTSTATUS Status;
458    KIRQL WaitIrql;
459    BOOLEAN Abandoned;
460
461    CurrentThread = KeGetCurrentThread();
462    WaitIrql = KeGetCurrentIrql();
463
464    /*
465     * Set up the timeout
466     * FIXME: Check for zero timeout
467     */
468    if (Timeout != NULL)
469      {
470        KeInitializeTimer(&CurrentThread->Timer);
471        KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
472      }
473    
474    do
475      {
476        KeAcquireDispatcherDatabaseLock(FALSE);
477
478        /*
479         * If we are going to wait alertably and a user apc is pending
480         * then return
481         */ 
482        if (Alertable && KiTestAlert())
483          {
484            KeReleaseDispatcherDatabaseLock(FALSE);
485            return(STATUS_USER_APC);
486          }
487
488        /*
489         * If the object is signalled
490         */
491        if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
492          {
493            if (Timeout != NULL)
494              {
495                KeCancelTimer(&CurrentThread->Timer);
496              }
497            KeReleaseDispatcherDatabaseLock(FALSE);
498            if (Abandoned == TRUE)
499              return(STATUS_ABANDONED_WAIT_0);
500            return(STATUS_WAIT_0);
501          }
502
503        /*
504         * Check if we have already timed out
505         */
506        if (Timeout != NULL && 
507            KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
508          {
509            KeCancelTimer(&CurrentThread->Timer);
510            KeReleaseDispatcherDatabaseLock(FALSE);
511            return(STATUS_TIMEOUT);
512          }
513        
514        /*
515         * Set up for a wait
516         */
517        CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
518        /* Append wait block to the KTHREAD wait block list */
519        CurrentThread->WaitBlockList = &CurrentThread->WaitBlock[0];
520        CurrentThread->WaitBlock[0].Object = Object;
521        CurrentThread->WaitBlock[0].Thread = CurrentThread;
522        CurrentThread->WaitBlock[0].WaitKey = STATUS_WAIT_0;
523        CurrentThread->WaitBlock[0].WaitType = WaitAny;
524        InsertTailList(&hdr->WaitListHead, 
525                       &CurrentThread->WaitBlock[0].WaitListEntry);
526        if (Timeout != NULL)
527          {
528            CurrentThread->WaitBlock[0].NextWaitBlock = 
529              &CurrentThread->WaitBlock[3];
530            CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
531            CurrentThread->WaitBlock[3].Thread = CurrentThread;
532            CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
533            CurrentThread->WaitBlock[3].WaitType = WaitAny;
534            CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
535            InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
536                           &CurrentThread->WaitBlock[3].WaitListEntry);
537          }
538        else
539          {
540            CurrentThread->WaitBlock[0].NextWaitBlock = NULL;
541          }
542        PsBlockThread(&Status, (UCHAR)Alertable, WaitMode, TRUE, WaitIrql);
543      } while (Status == STATUS_KERNEL_APC);
544    
545    if (Timeout != NULL)
546      {
547        KeCancelTimer(&CurrentThread->Timer);
548      }
549
550    DPRINT("Returning from KeWaitForSingleObject()\n");
551    return(Status);
552 }
553
554
555 NTSTATUS STDCALL
556 KeWaitForMultipleObjects(ULONG Count,
557                          PVOID Object[],
558                          WAIT_TYPE WaitType,
559                          KWAIT_REASON WaitReason,
560                          KPROCESSOR_MODE WaitMode,
561                          BOOLEAN Alertable,
562                          PLARGE_INTEGER Timeout,
563                          PKWAIT_BLOCK WaitBlockArray)
564 {
565   DISPATCHER_HEADER* hdr;
566   PKWAIT_BLOCK blk;
567   PKTHREAD CurrentThread;
568   ULONG CountSignaled;
569   ULONG i;
570   NTSTATUS Status;
571   KIRQL WaitIrql;
572   BOOLEAN Abandoned;
573
574   DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
575          "PsGetCurrentThread() %x\n",Count,Object,PsGetCurrentThread());
576
577   CountSignaled = 0;
578   CurrentThread = KeGetCurrentThread();
579   WaitIrql = KeGetCurrentIrql();
580
581   /*
582    * Work out where we are going to put the wait blocks
583    */
584   if (WaitBlockArray == NULL)
585     {
586       if (Count > THREAD_WAIT_OBJECTS)
587         {
588           DbgPrint("(%s:%d) Too many objects!\n",
589                    __FILE__,__LINE__);
590           return(STATUS_UNSUCCESSFUL);
591         }
592       WaitBlockArray = &CurrentThread->WaitBlock[0];
593     }
594   else
595     {
596       if (Count > EX_MAXIMUM_WAIT_OBJECTS)
597         {
598           DbgPrint("(%s:%d) Too many objects!\n",
599                    __FILE__,__LINE__);
600           return(STATUS_UNSUCCESSFUL);
601         }
602     }
603
604   /*
605    * Set up the timeout if required
606    */
607   if (Timeout != NULL)
608     {
609       KeInitializeTimer(&CurrentThread->Timer);
610       KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
611     }
612   
613   do 
614     {
615       KeAcquireDispatcherDatabaseLock(FALSE);
616
617        /*
618         * If we are going to wait alertably and a user apc is pending
619         * then return
620         */ 
621        if (Alertable && KiTestAlert())
622          {
623            KeReleaseDispatcherDatabaseLock(FALSE);
624            return(STATUS_USER_APC);
625          }
626
627        /*
628         * Check if the wait is already satisfied
629         */
630        for (i = 0; i < Count; i++)
631          {
632            hdr = (DISPATCHER_HEADER *)Object[i];
633            
634            if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
635              {
636                CountSignaled++;
637                
638                if (WaitType == WaitAny)
639                  {
640                    if (Timeout != NULL)
641                    {
642                         KeCancelTimer(&CurrentThread->Timer);
643                    }
644                    KeReleaseDispatcherDatabaseLock(FALSE);
645                    DPRINT("One object is already signaled!\n");
646                    if (Abandoned == TRUE)
647                      return(STATUS_ABANDONED_WAIT_0 + i);
648                    return(STATUS_WAIT_0 + i);
649                  }
650              }
651          }
652        
653        if ((WaitType == WaitAll) && (CountSignaled == Count))
654          {
655            if (Timeout != NULL)
656              {
657                KeCancelTimer(&CurrentThread->Timer);
658              }
659            KeReleaseDispatcherDatabaseLock(FALSE);
660            DPRINT("All objects are already signaled!\n");
661            return(STATUS_WAIT_0);
662          }
663     
664        /*
665         * Check if we have already timed out
666         */
667        if (Timeout != NULL && 
668            KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
669          {
670            KeCancelTimer(&CurrentThread->Timer);
671            KeReleaseDispatcherDatabaseLock(FALSE);
672            return(STATUS_TIMEOUT);
673          }
674
675        /* Append wait block to the KTHREAD wait block list */
676        CurrentThread->WaitBlockList = blk = WaitBlockArray;
677   
678        /*
679         * Set up the wait
680         */
681        CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
682        for (i = 0; i < Count; i++)
683          {
684            hdr = (DISPATCHER_HEADER *)Object[i];
685            
686            blk->Object = Object[i];
687            blk->Thread = CurrentThread;
688            blk->WaitKey = STATUS_WAIT_0 + i;
689            blk->WaitType = WaitType;
690            if (i == (Count - 1))
691              {
692                if (Timeout != NULL)
693                  {
694                    blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
695                  }
696                else
697                  {
698                    blk->NextWaitBlock = NULL;
699                  }
700              }
701            else
702              {
703                blk->NextWaitBlock = blk + 1;
704              }
705            
706            InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
707            
708            blk = blk->NextWaitBlock;
709          }
710        if (Timeout != NULL)
711          {
712            CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
713            CurrentThread->WaitBlock[3].Thread = CurrentThread;
714            CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
715            CurrentThread->WaitBlock[3].WaitType = WaitAny;
716            CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
717            InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
718                           &CurrentThread->WaitBlock[3].WaitListEntry);
719          }
720
721        PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql);
722     } while(Status == STATUS_KERNEL_APC);
723   
724   if (Timeout != NULL)
725     {
726       KeCancelTimer(&CurrentThread->Timer);
727     }
728
729   DPRINT("Returning from KeWaitForMultipleObjects()\n");
730   return(Status);
731 }
732
733 VOID KeInitializeDispatcher(VOID)
734 {
735    KeInitializeSpinLock(&DispatcherDatabaseLock);
736 }
737
738 NTSTATUS STDCALL
739 NtWaitForMultipleObjects(IN ULONG Count,
740                          IN HANDLE Object [],
741                          IN CINT WaitType,
742                          IN BOOLEAN Alertable,
743                          IN PLARGE_INTEGER Time)
744 {
745    KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
746    PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
747    NTSTATUS Status;
748    ULONG i, j;
749
750    DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
751           "Time %x)\n", Count,Object,Alertable,Time);
752
753    if (Count > EX_MAXIMUM_WAIT_OBJECTS)
754      return STATUS_UNSUCCESSFUL;
755
756    /* reference all objects */
757    for (i = 0; i < Count; i++)
758      {
759         Status = ObReferenceObjectByHandle(Object[i],
760                                            SYNCHRONIZE,
761                                            NULL,
762                                            UserMode,
763                                            &ObjectPtrArray[i],
764                                            NULL);
765         if (Status != STATUS_SUCCESS)
766           {
767              /* dereference all referenced objects */
768              for (j = 0; j < i; j++)
769                {
770                   ObDereferenceObject(ObjectPtrArray[j]);
771                }
772
773              return(Status);
774           }
775      }
776
777    Status = KeWaitForMultipleObjects(Count,
778                                      ObjectPtrArray,
779                                      WaitType,
780                                      UserRequest,
781                                      UserMode,
782                                      Alertable,
783                                      Time,
784                                      WaitBlockArray);
785
786    /* dereference all objects */
787    for (i = 0; i < Count; i++)
788      {
789         ObDereferenceObject(ObjectPtrArray[i]);
790      }
791
792    return(Status);
793 }
794
795
796 NTSTATUS STDCALL
797 NtWaitForSingleObject(IN HANDLE Object,
798                       IN BOOLEAN Alertable,
799                       IN PLARGE_INTEGER Time)
800 {
801    PVOID ObjectPtr;
802    NTSTATUS Status;
803    
804    DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
805           Object,Alertable,Time);
806    
807    Status = ObReferenceObjectByHandle(Object,
808                                       SYNCHRONIZE,
809                                       NULL,
810                                       UserMode,
811                                       &ObjectPtr,
812                                       NULL);
813    if (!NT_SUCCESS(Status))
814      {
815         return(Status);
816      }
817    
818    Status = KeWaitForSingleObject(ObjectPtr,
819                                   UserMode,
820                                   UserMode,
821                                   Alertable,
822                                   Time);
823    
824    ObDereferenceObject(ObjectPtr);
825    
826    return(Status);
827 }
828
829
830 NTSTATUS STDCALL
831 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
832                                IN HANDLE WaitObject,
833                                IN BOOLEAN Alertable,
834                                IN PLARGE_INTEGER Time)
835 {
836    KPROCESSOR_MODE ProcessorMode;
837    DISPATCHER_HEADER* hdr;
838    PVOID SignalObj;
839    PVOID WaitObj;
840    NTSTATUS Status;
841
842    ProcessorMode = ExGetPreviousMode();
843    Status = ObReferenceObjectByHandle(SignalObject,
844                                       0,
845                                       NULL,
846                                       ProcessorMode,
847                                       &SignalObj,
848                                       NULL);
849    if (!NT_SUCCESS(Status))
850      {
851         return Status;
852      }
853
854    Status = ObReferenceObjectByHandle(WaitObject,
855                                       SYNCHRONIZE,
856                                       NULL,
857                                       ProcessorMode,
858                                       &WaitObj,
859                                       NULL);
860    if (!NT_SUCCESS(Status))
861      {
862         ObDereferenceObject(SignalObj);
863         return Status;
864      }
865
866    hdr = (DISPATCHER_HEADER *)SignalObj;
867    switch (hdr->Type)
868      {
869       case InternalNotificationEvent:
870       case InternalSynchronizationEvent:
871         KeSetEvent(SignalObj,
872                    EVENT_INCREMENT,
873                    TRUE);
874         break;
875
876       case InternalMutexType:
877         KeReleaseMutex(SignalObj,
878                        TRUE);
879         break;
880
881       case InternalSemaphoreType:
882         KeReleaseSemaphore(SignalObj,
883                            SEMAPHORE_INCREMENT,
884                            1,
885                            TRUE);
886         break;
887
888       default:
889         ObDereferenceObject(SignalObj);
890         ObDereferenceObject(WaitObj);
891         return STATUS_OBJECT_TYPE_MISMATCH;
892      }
893
894    Status = KeWaitForSingleObject(WaitObj,
895                                   UserRequest,
896                                   ProcessorMode,
897                                   Alertable,
898                                   Time);
899
900    ObDereferenceObject(SignalObj);
901    ObDereferenceObject(WaitObj);
902
903    return Status;
904 }
905
906 #endif /* LIBCAPTIVE */