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