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