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