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)
9 * 12/1/99: Phillip Susi: Fixed wake code in KeDispatcherObjectWake
10 * so that things like KeWaitForXXX() return the correct value
13 /* NOTES ********************************************************************
17 /* INCLUDES ******************************************************************/
22 #include <internal/debug.h>
24 /* GLOBALS ******************************************************************/
26 static KSPIN_LOCK DispatcherDatabaseLock
;
28 #define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
29 #define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
31 extern POBJECT_TYPE EXPORTED ExMutantObjectType
;
32 extern POBJECT_TYPE EXPORTED ExSemaphoreType
;
33 extern POBJECT_TYPE EXPORTED ExTimerType
;
35 /* FUNCTIONS *****************************************************************/
37 VOID
KeInitializeDispatcherHeader(DISPATCHER_HEADER
* Header
,
42 Header
->Type
= (UCHAR
)Type
;
45 Header
->Size
= (UCHAR
)Size
;
46 Header
->SignalState
= SignalState
;
47 InitializeListHead(&(Header
->WaitListHead
));
52 KeAcquireDispatcherDatabaseLock(VOID
)
54 * PURPOSE: Acquires the dispatcher database lock for the caller
59 DPRINT("KeAcquireDispatcherDatabaseLock()\n");
61 KeAcquireSpinLock (&DispatcherDatabaseLock
, &OldIrql
);
67 KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID
)
69 * PURPOSE: Acquires the dispatcher database lock for the caller
72 DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n");
74 KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock
);
79 KeReleaseDispatcherDatabaseLock(KIRQL OldIrql
)
81 DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql
);
83 KeReleaseSpinLock(&DispatcherDatabaseLock
, OldIrql
);
88 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID
)
90 DPRINT("KeReleaseDispatcherDatabaseLock()\n");
92 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock
);
97 KiSideEffectsBeforeWake(DISPATCHER_HEADER
* hdr
,
100 * FUNCTION: Perform side effects on object before a wait for a thread is
104 BOOLEAN Abandoned
= FALSE
;
108 case InternalSynchronizationEvent
:
109 hdr
->SignalState
= 0;
112 case InternalQueueType
:
113 case InternalSemaphoreType
:
117 case InternalProcessType
:
120 case InternalThreadType
:
123 case InternalNotificationEvent
:
126 case InternalSynchronizationTimer
:
127 hdr
->SignalState
= FALSE
;
130 case InternalNotificationTimer
:
133 case InternalMutexType
:
137 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
139 assert(hdr
->SignalState
<= 1);
140 if (hdr
->SignalState
== 0)
144 DPRINT("Thread == NULL!\n");
147 Abandoned
= Mutex
->Abandoned
;
149 InsertTailList(&Thread
->MutantListHead
, &Mutex
->MutantListEntry
);
150 Mutex
->OwnerThread
= Thread
;
151 Mutex
->Abandoned
= FALSE
;
157 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__
, __LINE__
, hdr
);
165 KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
,
168 if (hdr
->Type
== InternalMutexType
)
172 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
174 assert(hdr
->SignalState
<= 1);
176 if ((hdr
->SignalState
< 1 && Mutex
->OwnerThread
== Thread
) || hdr
->SignalState
== 1)
186 if (hdr
->SignalState
<= 0)
196 VOID
KeRemoveAllWaitsThread(PETHREAD Thread
, NTSTATUS WaitStatus
, BOOL Unblock
)
198 PKWAIT_BLOCK WaitBlock
, PrevWaitBlock
;
199 BOOLEAN WasWaiting
= FALSE
;
202 OldIrql
= KeAcquireDispatcherDatabaseLock ();
204 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
205 if (WaitBlock
!= NULL
)
209 while (WaitBlock
!= NULL
)
211 if (WaitBlock
->WaitListEntry
.Flink
!= NULL
&& WaitBlock
->WaitListEntry
.Blink
!= NULL
)
213 RemoveEntryList (&WaitBlock
->WaitListEntry
);
214 WaitBlock
->WaitListEntry
.Flink
= WaitBlock
->WaitListEntry
.Blink
= NULL
;
216 PrevWaitBlock
= WaitBlock
;
217 WaitBlock
= WaitBlock
->NextWaitBlock
;
218 PrevWaitBlock
->NextWaitBlock
= NULL
;
220 Thread
->Tcb
.WaitBlockList
= NULL
;
222 if (WasWaiting
&& Unblock
)
224 PsUnblockThread(Thread
, &WaitStatus
);
227 KeReleaseDispatcherDatabaseLock (OldIrql
);
231 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER
* hdr
,
235 PKWAIT_BLOCK WaiterHead
;
236 PLIST_ENTRY EnumEntry
;
240 BOOLEAN WakedAny
= FALSE
;
242 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr
);
243 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
244 hdr
->WaitListHead
.Flink
, hdr
->WaitListHead
.Blink
);
246 if (IsListEmpty(&hdr
->WaitListHead
))
251 //enum waiters for this dispatcher object
252 EnumEntry
= hdr
->WaitListHead
.Flink
;
253 while (EnumEntry
!= &hdr
->WaitListHead
&& (WakeAll
|| !WakedAny
))
255 WaiterHead
= CONTAINING_RECORD(EnumEntry
, KWAIT_BLOCK
, WaitListEntry
);
256 DPRINT("current_entry %x current %x\n", EnumEntry
, WaiterHead
);
257 EnumEntry
= EnumEntry
->Flink
;
258 assert(WaiterHead
->Thread
!= NULL
);
259 assert(WaiterHead
->Thread
->WaitBlockList
!= NULL
);
263 if (WaiterHead
->WaitType
== WaitAny
)
265 DPRINT("WaitAny: Remove all wait blocks.\n");
266 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
268 if (Waiter
->WaitListEntry
.Flink
!= NULL
&& Waiter
->WaitListEntry
.Blink
!= NULL
)
270 RemoveEntryList(&Waiter
->WaitListEntry
);
271 Waiter
->WaitListEntry
.Flink
= Waiter
->WaitListEntry
.Blink
= NULL
;
275 WaiterHead
->Thread
->WaitBlockList
= NULL
;
278 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
279 * but thats ok since WakeAll objects has no sideeffects.
281 Abandoned
= KiSideEffectsBeforeWake(hdr
, WaiterHead
->Thread
) ? TRUE
: Abandoned
;
285 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
289 //all WaitAll obj. for thread need to be signaled to satisfy a wake
290 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
292 //no need to check hdr since it has to be signaled
293 if (Waiter
->WaitType
== WaitAll
&& Waiter
->Object
!= hdr
)
295 if (!KiIsObjectSignalled(Waiter
->Object
, Waiter
->Thread
))
305 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
307 if (Waiter
->WaitListEntry
.Flink
!= NULL
&& Waiter
->WaitListEntry
.Blink
!= NULL
)
309 RemoveEntryList(&Waiter
->WaitListEntry
);
310 Waiter
->WaitListEntry
.Flink
= Waiter
->WaitListEntry
.Blink
= NULL
;
313 if (Waiter
->WaitType
== WaitAll
)
315 Abandoned
= KiSideEffectsBeforeWake(Waiter
->Object
, Waiter
->Thread
)
319 //no WaitAny objects can possibly be signaled since we are here
320 assert(!(Waiter
->WaitType
== WaitAny
321 && KiIsObjectSignalled(Waiter
->Object
, Waiter
->Thread
)));
324 WaiterHead
->Thread
->WaitBlockList
= NULL
;
328 if (WaiterHead
->Thread
->WaitBlockList
== NULL
)
330 Status
= WaiterHead
->WaitKey
;
333 DPRINT("Abandoned mutex among objects");
334 Status
+= STATUS_ABANDONED_WAIT_0
;
338 DPRINT("Waking %x status = %x\n", WaiterHead
->Thread
, Status
);
339 PsUnblockThread(CONTAINING_RECORD(WaiterHead
->Thread
, ETHREAD
, Tcb
), &Status
);
347 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
349 * FUNCTION: Wake threads waiting on a dispatcher object
350 * NOTE: The exact semantics of waking are dependant on the type of object
355 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
356 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
357 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
358 DPRINT("hdr->Type %x\n",hdr
->Type
);
361 case InternalNotificationEvent
:
362 return(KeDispatcherObjectWakeAll(hdr
));
364 case InternalNotificationTimer
:
365 return(KeDispatcherObjectWakeAll(hdr
));
367 case InternalSynchronizationEvent
:
368 return(KeDispatcherObjectWakeOne(hdr
));
370 case InternalSynchronizationTimer
:
371 return(KeDispatcherObjectWakeOne(hdr
));
373 case InternalQueueType
:
374 case InternalSemaphoreType
:
375 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
376 if(hdr
->SignalState
>0)
380 DPRINT("Waking one semaphore waiter\n");
381 Ret
= KeDispatcherObjectWakeOne(hdr
);
382 } while(hdr
->SignalState
> 0 && Ret
) ;
387 case InternalProcessType
:
388 return(KeDispatcherObjectWakeAll(hdr
));
390 case InternalThreadType
:
391 return(KeDispatcherObjectWakeAll(hdr
));
393 case InternalMutexType
:
394 return(KeDispatcherObjectWakeOne(hdr
));
396 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
406 KeWaitForSingleObject(PVOID Object
,
407 KWAIT_REASON WaitReason
,
408 KPROCESSOR_MODE WaitMode
,
410 PLARGE_INTEGER Timeout
)
412 * FUNCTION: Puts the current thread into a wait state until the
413 * given dispatcher object is set to signalled
415 * Object = Object to wait on
416 * WaitReason = Reason for the wait (debugging aid)
417 * WaitMode = Can be KernelMode or UserMode, if UserMode then
418 * user-mode APCs can be delivered and the thread's
419 * stack can be paged out
420 * Altertable = Specifies if the wait is a alertable
421 * Timeout = Optional timeout value
425 return KeWaitForMultipleObjects(1,
438 KiGetWaitableObjectFromObject(PVOID Object
)
440 //special case when waiting on file objects
441 if ( ((PDISPATCHER_HEADER
)Object
)->Type
== InternalFileType
)
443 return &((PFILE_OBJECT
)Object
)->Event
;
451 KiIsObjectWaitable(PVOID Object
)
453 POBJECT_HEADER Header
;
454 Header
= BODY_TO_HEADER(Object
);
455 if (Header
->ObjectType
== ExEventObjectType
||
456 Header
->ObjectType
== ExIoCompletionType
||
457 Header
->ObjectType
== ExMutantObjectType
||
458 Header
->ObjectType
== ExSemaphoreType
||
459 Header
->ObjectType
== ExTimerType
||
460 Header
->ObjectType
== PsProcessType
||
461 Header
->ObjectType
== PsThreadType
||
462 Header
->ObjectType
== IoFileObjectType
)
476 KeWaitForMultipleObjects(ULONG Count
,
479 KWAIT_REASON WaitReason
,
480 KPROCESSOR_MODE WaitMode
,
482 PLARGE_INTEGER Timeout
,
483 PKWAIT_BLOCK WaitBlockArray
)
485 DISPATCHER_HEADER
*hdr
;
487 PKTHREAD CurrentThread
;
495 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
496 "PsGetCurrentThread() %x\n", Count
, Object
, PsGetCurrentThread());
498 CurrentThread
= KeGetCurrentThread();
499 WaitIrql
= KeGetCurrentIrql();
502 * Work out where we are going to put the wait blocks
504 if (WaitBlockArray
== NULL
)
506 if (Count
> THREAD_WAIT_OBJECTS
)
508 DPRINT("(%s:%d) Too many objects!\n", __FILE__
, __LINE__
);
509 return (STATUS_UNSUCCESSFUL
);
511 WaitBlockArray
= &CurrentThread
->WaitBlock
[0];
515 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
517 DPRINT("(%s:%d) Too many objects!\n", __FILE__
, __LINE__
);
518 return (STATUS_UNSUCCESSFUL
);
523 * Set up the timeout if required
525 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
527 KeSetTimer(&CurrentThread
->Timer
, *Timeout
, NULL
);
532 if (CurrentThread
->WaitNext
)
534 OldIrql
= CurrentThread
->WaitIrql
;
535 CurrentThread
->WaitNext
= 0;
536 CurrentThread
->WaitIrql
= PASSIVE_LEVEL
;
540 OldIrql
= KeAcquireDispatcherDatabaseLock ();
544 * If we are going to wait alertably and a user apc is pending
547 if (Alertable
&& KiTestAlert())
549 KeReleaseDispatcherDatabaseLock(OldIrql
);
550 return (STATUS_USER_APC
);
554 * Check if the wait is (already) satisfied
558 for (i
= 0; i
< Count
; i
++)
560 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
562 if (KiIsObjectSignalled(hdr
, CurrentThread
))
566 if (WaitType
== WaitAny
)
568 Abandoned
= KiSideEffectsBeforeWake(hdr
, CurrentThread
) ? TRUE
: Abandoned
;
570 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
572 KeCancelTimer(&CurrentThread
->Timer
);
575 KeReleaseDispatcherDatabaseLock(OldIrql
);
577 DPRINT("One object is (already) signaled!\n");
578 if (Abandoned
== TRUE
)
580 return (STATUS_ABANDONED_WAIT_0
+ i
);
583 return (STATUS_WAIT_0
+ i
);
589 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
591 for (i
= 0; i
< Count
; i
++)
593 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
594 Abandoned
= KiSideEffectsBeforeWake(hdr
, CurrentThread
) ? TRUE
: Abandoned
;
597 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
599 KeCancelTimer(&CurrentThread
->Timer
);
602 KeReleaseDispatcherDatabaseLock(OldIrql
);
603 DPRINT("All objects are (already) signaled!\n");
605 if (Abandoned
== TRUE
)
607 return (STATUS_ABANDONED_WAIT_0
);
610 return (STATUS_WAIT_0
);
613 //zero timeout is used for testing if the object(s) can be immediately acquired
614 if (Timeout
!= NULL
&& Timeout
->QuadPart
== 0)
616 KeReleaseDispatcherDatabaseLock(OldIrql
);
617 return STATUS_TIMEOUT
;
621 * Check if we have already timed out
623 if (Timeout
!= NULL
&& KiIsObjectSignalled(&CurrentThread
->Timer
.Header
, CurrentThread
))
625 KiSideEffectsBeforeWake(&CurrentThread
->Timer
.Header
, CurrentThread
);
626 KeCancelTimer(&CurrentThread
->Timer
);
627 KeReleaseDispatcherDatabaseLock(OldIrql
);
628 return (STATUS_TIMEOUT
);
631 /* Append wait block to the KTHREAD wait block list */
632 CurrentThread
->WaitBlockList
= blk
= WaitBlockArray
;
637 CurrentThread
->WaitStatus
= STATUS_UNSUCCESSFUL
;
639 for (i
= 0; i
< Count
; i
++)
641 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
643 blk
->Object
= KiGetWaitableObjectFromObject(Object
[i
]);
644 blk
->Thread
= CurrentThread
;
645 blk
->WaitKey
= (USHORT
)(STATUS_WAIT_0
+ i
);
646 blk
->WaitType
= (USHORT
)WaitType
;
648 if (i
== (Count
- 1))
652 blk
->NextWaitBlock
= &CurrentThread
->WaitBlock
[3];
656 blk
->NextWaitBlock
= NULL
;
661 blk
->NextWaitBlock
= blk
+ 1;
665 * add wait block to disp. obj. wait list
666 * Use FIFO for all waits except for queues which use LIFO
668 if (WaitReason
== WrQueue
)
670 InsertHeadList(&hdr
->WaitListHead
, &blk
->WaitListEntry
);
674 InsertTailList(&hdr
->WaitListHead
, &blk
->WaitListEntry
);
677 blk
= blk
->NextWaitBlock
;
682 CurrentThread
->WaitBlock
[3].Object
= (PVOID
) & CurrentThread
->Timer
;
683 CurrentThread
->WaitBlock
[3].Thread
= CurrentThread
;
684 CurrentThread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
685 CurrentThread
->WaitBlock
[3].WaitType
= WaitAny
;
686 CurrentThread
->WaitBlock
[3].NextWaitBlock
= NULL
;
688 InsertTailList(&CurrentThread
->Timer
.Header
.WaitListHead
,
689 &CurrentThread
->WaitBlock
[3].WaitListEntry
);
693 if (CurrentThread
->Queue
)
695 CurrentThread
->Queue
->CurrentCount
--;
696 if (WaitReason
!= WrQueue
&& CurrentThread
->Queue
->CurrentCount
< CurrentThread
->Queue
->MaximumCount
&&
697 !IsListEmpty(&CurrentThread
->Queue
->EntryListHead
))
699 KeDispatcherObjectWake(&CurrentThread
->Queue
->Header
);
703 PsBlockThread(&Status
, Alertable
, WaitMode
, TRUE
, WaitIrql
, (UCHAR
)WaitReason
);
706 if (CurrentThread
->Queue
)
708 CurrentThread
->Queue
->CurrentCount
++;
713 while (Status
== STATUS_KERNEL_APC
);
717 KeCancelTimer(&CurrentThread
->Timer
);
720 DPRINT("Returning from KeWaitForMultipleObjects()\n");
724 VOID
KeInitializeDispatcher(VOID
)
726 KeInitializeSpinLock(&DispatcherDatabaseLock
);
730 NtWaitForMultipleObjects(IN ULONG Count
,
732 IN WAIT_TYPE WaitType
,
733 IN BOOLEAN Alertable
,
734 IN PLARGE_INTEGER UnsafeTime
)
736 KWAIT_BLOCK WaitBlockArray
[EX_MAXIMUM_WAIT_OBJECTS
];
737 PVOID ObjectPtrArray
[EX_MAXIMUM_WAIT_OBJECTS
];
740 KPROCESSOR_MODE WaitMode
;
743 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
744 "Time %x)\n", Count
,Object
,Alertable
,Time
);
746 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
747 return STATUS_UNSUCCESSFUL
;
751 Status
= MmCopyFromCaller(&Time
, UnsafeTime
, sizeof(LARGE_INTEGER
));
752 if (!NT_SUCCESS(Status
))
758 WaitMode
= ExGetPreviousMode();
760 /* reference all objects */
761 for (i
= 0; i
< Count
; i
++)
763 Status
= ObReferenceObjectByHandle(Object
[i
],
769 if (!NT_SUCCESS(Status
) || !KiIsObjectWaitable(ObjectPtrArray
[i
]))
771 if (NT_SUCCESS(Status
))
773 DPRINT1("Waiting for object type '%wZ' is not supported\n",
774 &BODY_TO_HEADER(ObjectPtrArray
[i
])->ObjectType
->TypeName
);
775 Status
= STATUS_HANDLE_NOT_WAITABLE
;
778 /* dereference all referenced objects */
779 for (j
= 0; j
< i
; j
++)
781 ObDereferenceObject(ObjectPtrArray
[j
]);
788 Status
= KeWaitForMultipleObjects(Count
,
794 UnsafeTime
? &Time
: NULL
,
797 /* dereference all objects */
798 for (i
= 0; i
< Count
; i
++)
800 ObDereferenceObject(ObjectPtrArray
[i
]);
811 NtWaitForSingleObject(IN HANDLE Object
,
812 IN BOOLEAN Alertable
,
813 IN PLARGE_INTEGER UnsafeTime
)
817 KPROCESSOR_MODE WaitMode
;
820 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
821 Object
,Alertable
,Time
);
825 Status
= MmCopyFromCaller(&Time
, UnsafeTime
, sizeof(LARGE_INTEGER
));
826 if (!NT_SUCCESS(Status
))
832 WaitMode
= ExGetPreviousMode();
834 Status
= ObReferenceObjectByHandle(Object
,
840 if (!NT_SUCCESS(Status
))
844 if (!KiIsObjectWaitable(ObjectPtr
))
846 DPRINT1("Waiting for object type '%wZ' is not supported\n",
847 &BODY_TO_HEADER(ObjectPtr
)->ObjectType
->TypeName
);
848 Status
= STATUS_HANDLE_NOT_WAITABLE
;
852 Status
= KeWaitForSingleObject(ObjectPtr
,
856 UnsafeTime
? &Time
: NULL
);
859 ObDereferenceObject(ObjectPtr
);
866 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject
,
867 IN HANDLE WaitObject
,
868 IN BOOLEAN Alertable
,
869 IN PLARGE_INTEGER Time
)
871 KPROCESSOR_MODE WaitMode
;
872 DISPATCHER_HEADER
* hdr
;
877 WaitMode
= ExGetPreviousMode();
878 Status
= ObReferenceObjectByHandle(SignalObject
,
884 if (!NT_SUCCESS(Status
))
889 Status
= ObReferenceObjectByHandle(WaitObject
,
895 if (!NT_SUCCESS(Status
))
897 ObDereferenceObject(SignalObj
);
901 hdr
= (DISPATCHER_HEADER
*)SignalObj
;
904 case InternalNotificationEvent
:
905 case InternalSynchronizationEvent
:
906 KeSetEvent(SignalObj
,
911 case InternalMutexType
:
912 KeReleaseMutex(SignalObj
,
916 case InternalSemaphoreType
:
917 KeReleaseSemaphore(SignalObj
,
924 ObDereferenceObject(SignalObj
);
925 ObDereferenceObject(WaitObj
);
926 return STATUS_OBJECT_TYPE_MISMATCH
;
929 Status
= KeWaitForSingleObject(WaitObj
,
935 ObDereferenceObject(SignalObj
);
936 ObDereferenceObject(WaitObj
);