mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
714 lines
22 KiB
714 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
obwait.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the generic wait system services.
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 12-May-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "obp.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, NtWaitForSingleObject)
|
|
#endif
|
|
|
|
extern POBJECT_TYPE ExEventObjectType;
|
|
extern POBJECT_TYPE ExMutantObjectType;
|
|
extern POBJECT_TYPE ExSemaphoreObjectType;
|
|
|
|
|
|
NTSTATUS
|
|
NtSignalAndWaitForSingleObject(
|
|
IN HANDLE SignalHandle,
|
|
IN HANDLE WaitHandle,
|
|
IN BOOLEAN Alertable,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function atomically signals the specified signal object and then
|
|
waits until the specified wait object attains a state of Signaled. An
|
|
optional timeout can also be specified. If a timeout is not specified,
|
|
then the wait will not be satisfied until the wait object attains a
|
|
state of Signaled. If a timeout is specified, and the wait object has
|
|
not attained a state of Signaled when the timeout expires, then the
|
|
wait is automatically satisfied. If an explicit timeout value of zero
|
|
is specified, then no wait will occur if the wait cannot be satisfied
|
|
immediately. The wait can also be specified as alertable.
|
|
|
|
Arguments:
|
|
|
|
SignalHandle - supplies the handle of the signal object.
|
|
|
|
WaitHandle - Supplies the handle for the wait object.
|
|
|
|
Alertable - Supplies a boolean value that specifies whether the wait
|
|
is alertable.
|
|
|
|
Timeout - Supplies an pointer to an absolute or relative time over
|
|
which the wait is to occur.
|
|
|
|
Return Value:
|
|
|
|
The wait completion status. A value of STATUS_TIMEOUT is returned if a
|
|
timeout occurred. A value of STATUS_SUCCESS is returned if the specified
|
|
object satisfied the wait. A value of STATUS_ALERTED is returned if the
|
|
wait was aborted to deliver an alert to the current thread. A value of
|
|
STATUS_USER_APC is returned if the wait was aborted to deliver a user
|
|
APC to the current thread.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
PVOID RealObject;
|
|
PVOID SignalObject;
|
|
POBJECT_HEADER SignalObjectHeader;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER TimeoutValue;
|
|
PVOID WaitObject;
|
|
POBJECT_HEADER WaitObjectHeader;
|
|
|
|
//
|
|
// Establish an exception handler and probe the specified timeout value
|
|
// if necessary. If the probe fails, then return the exception code as
|
|
// the service status.
|
|
//
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
if ((ARGUMENT_PRESENT(Timeout)) && (PreviousMode != KernelMode)) {
|
|
try {
|
|
TimeoutValue = ProbeAndReadLargeInteger(Timeout);
|
|
Timeout = &TimeoutValue;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reference the signal object by handle.
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(SignalHandle,
|
|
0,
|
|
NULL,
|
|
PreviousMode,
|
|
&SignalObject,
|
|
&HandleInformation);
|
|
|
|
//
|
|
// If the reference was successful, then reference the wait object by
|
|
// handle.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ObReferenceObjectByHandle(WaitHandle,
|
|
SYNCHRONIZE,
|
|
NULL,
|
|
PreviousMode,
|
|
&WaitObject,
|
|
NULL);
|
|
|
|
//
|
|
// If the reference was successful, then determine the real wait
|
|
// object, check the signal object access, signal the signal object,
|
|
// and wait for the real wait object.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
WaitObjectHeader = OBJECT_TO_OBJECT_HEADER(WaitObject);
|
|
RealObject = WaitObjectHeader->Type->DefaultObject;
|
|
if ((LONG)RealObject >= 0) {
|
|
RealObject = (PVOID)((PCHAR)WaitObject + (ULONG)RealObject);
|
|
}
|
|
|
|
//
|
|
// If the signal object is an event, then check for modify access
|
|
// and set the event. Otherwise, if the signal object is a mutant,
|
|
// then attempt to release ownership of the mutant. Otherwise, if
|
|
// the object is a semaphore, then check for modify access and
|
|
// release the semaphore. Otherwise, the signal objet is invalid.
|
|
//
|
|
|
|
SignalObjectHeader = OBJECT_TO_OBJECT_HEADER(SignalObject);
|
|
Status = STATUS_ACCESS_DENIED;
|
|
if (SignalObjectHeader->Type == ExEventObjectType) {
|
|
|
|
//
|
|
// Check for access to the specified event object,
|
|
//
|
|
|
|
if ((PreviousMode != KernelMode) &&
|
|
(SeComputeDeniedAccesses(HandleInformation.GrantedAccess,
|
|
EVENT_MODIFY_STATE) != 0)) {
|
|
goto WaitExit;
|
|
}
|
|
|
|
//
|
|
// If the wait object is also an event, the wait is not
|
|
// alertable, and no timeout was specified, then the event
|
|
// pair path can be used. Otherwise, set the event and wait
|
|
// atomically.
|
|
//
|
|
|
|
if ((WaitObjectHeader->Type == ExEventObjectType) &&
|
|
(Alertable == FALSE) &&
|
|
(Timeout == NULL)) {
|
|
Status = KiSetServerWaitClientEvent((PKEVENT)SignalObject,
|
|
(PKEVENT)RealObject,
|
|
PreviousMode);
|
|
|
|
goto WaitExit;
|
|
}
|
|
|
|
//
|
|
// Set the specified event and wait atomically.
|
|
//
|
|
|
|
KeSetEvent((PKEVENT)SignalObject, EVENT_INCREMENT, TRUE);
|
|
|
|
} else if (SignalObjectHeader->Type == ExMutantObjectType) {
|
|
|
|
//
|
|
// Release the specified mutant and wait atomically.
|
|
//
|
|
// N.B. The release will only be successful if the current
|
|
// thread is the owner of the mutant.
|
|
//
|
|
|
|
try {
|
|
KeReleaseMutant((PKMUTANT)SignalObject,
|
|
MUTANT_INCREMENT,
|
|
FALSE,
|
|
TRUE);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
goto WaitExit;
|
|
}
|
|
|
|
} else if (SignalObjectHeader->Type == ExSemaphoreObjectType) {
|
|
|
|
//
|
|
// Check for access to the specified semaphore object,
|
|
//
|
|
|
|
if ((PreviousMode != KernelMode) &&
|
|
(SeComputeDeniedAccesses(HandleInformation.GrantedAccess,
|
|
SEMAPHORE_MODIFY_STATE) != 0)) {
|
|
goto WaitExit;
|
|
}
|
|
|
|
//
|
|
// Release the specified semaphore and wait atomically.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// If the wait object is also a semaphore, the wait is
|
|
// not alertable, and no timeout was specified, then
|
|
// the semaphore path can be used. Otherwise, release
|
|
// the semaphore and wait atomically.
|
|
//
|
|
|
|
if ((WaitObjectHeader->Type == ExSemaphoreObjectType) &&
|
|
(Alertable == FALSE) &&
|
|
(Timeout == NULL)) {
|
|
Status = KeReleaseWaitForSemaphore((PKSEMAPHORE)SignalObject,
|
|
(PKSEMAPHORE)RealObject,
|
|
UserRequest,
|
|
PreviousMode);
|
|
|
|
goto WaitExit;
|
|
}
|
|
|
|
//
|
|
// Release the specified semaphore and wait atomically.
|
|
//
|
|
|
|
KeReleaseSemaphore((PKSEMAPHORE)SignalObject,
|
|
SEMAPHORE_INCREMENT,
|
|
1,
|
|
TRUE);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
goto WaitExit;
|
|
}
|
|
|
|
} else {
|
|
Status =
|
|
STATUS_OBJECT_TYPE_MISMATCH;
|
|
goto WaitExit;
|
|
}
|
|
|
|
Status = KeWaitForSingleObject(RealObject,
|
|
UserRequest,
|
|
PreviousMode,
|
|
Alertable,
|
|
Timeout);
|
|
|
|
WaitExit:
|
|
ObDereferenceObject(WaitObject);
|
|
}
|
|
|
|
ObDereferenceObject(SignalObject);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NtWaitForSingleObject (
|
|
IN HANDLE Handle,
|
|
IN BOOLEAN Alertable,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function waits until the specified object attains a state of
|
|
Signaled. An optional timeout can also be specified. If a timeout
|
|
is not specified, then the wait will not be satisfied until the
|
|
object attains a state of Signaled. If a timeout is specified, and
|
|
the object has not attained a state of Signaled when the timeout
|
|
expires, then the wait is automatically satisfied. If an explicit
|
|
timeout value of zero is specified, then no wait will occur if the
|
|
wait cannot be satisfied immediately. The wait can also be specified
|
|
as alertable.
|
|
|
|
Arguments:
|
|
|
|
Handle - Supplies the handle for the wait object.
|
|
|
|
Alertable - Supplies a boolean value that specifies whether the wait
|
|
is alertable.
|
|
|
|
Timeout - Supplies an pointer to an absolute or relative time over
|
|
which the wait is to occur.
|
|
|
|
Return Value:
|
|
|
|
The wait completion status. A value of STATUS_TIMEOUT is returned if a
|
|
timeout occurred. A value of STATUS_SUCCESS is returned if the specified
|
|
object satisfied the wait. A value of STATUS_ALERTED is returned if the
|
|
wait was aborted to deliver an alert to the current thread. A value of
|
|
STATUS_USER_APC is returned if the wait was aborted to deliver a user
|
|
APC to the current thread.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PVOID Object;
|
|
POBJECT_HEADER ObjectHeader;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER TimeoutValue;
|
|
PVOID WaitObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get previous processor mode and probe and capture timeout argument
|
|
// if necessary.
|
|
//
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
if ((ARGUMENT_PRESENT(Timeout)) && (PreviousMode != KernelMode)) {
|
|
try {
|
|
TimeoutValue = ProbeAndReadLargeInteger(Timeout);
|
|
Timeout = &TimeoutValue;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get a referenced pointer to the specified object with synchronize
|
|
// access.
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(Handle,
|
|
SYNCHRONIZE,
|
|
NULL,
|
|
PreviousMode,
|
|
&Object,
|
|
NULL);
|
|
|
|
//
|
|
// If access is granted, then check to determine if the specified object
|
|
// can be waited on.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
WaitObject = ObjectHeader->Type->DefaultObject;
|
|
if ((LONG)WaitObject >= 0) {
|
|
WaitObject = (PVOID)((PCHAR)Object + (ULONG)WaitObject);
|
|
}
|
|
|
|
Status = KeWaitForSingleObject(WaitObject,
|
|
UserRequest,
|
|
PreviousMode,
|
|
Alertable,
|
|
Timeout);
|
|
|
|
ObDereferenceObject(Object);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NtWaitForMultipleObjects (
|
|
IN ULONG Count,
|
|
IN HANDLE Handles[],
|
|
IN WAIT_TYPE WaitType,
|
|
IN BOOLEAN Alertable,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function waits until the specified objects attain a state of
|
|
Signaled. The wait can be specified to wait until all of the objects
|
|
attain a state of Signaled or until one of the objects attains a state
|
|
of Signaled. An optional timeout can also be specified. If a timeout
|
|
is not specified, then the wait will not be satisfied until the objects
|
|
attain a state of Signaled. If a timeout is specified, and the objects
|
|
have not attained a state of Signaled when the timeout expires, then
|
|
the wait is automatically satisfied. If an explicit timeout value of
|
|
zero is specified, then no wait will occur if the wait cannot be satisfied
|
|
immediately. The wait can also be specified as alertable.
|
|
|
|
Arguments:
|
|
|
|
Count - Supplies a count of the number of objects that are to be waited
|
|
on.
|
|
|
|
Handles[] - Supplies an array of handles to wait objects.
|
|
|
|
WaitType - Supplies the type of wait to perform (WaitAll, WaitAny).
|
|
|
|
Alertable - Supplies a boolean value that specifies whether the wait is
|
|
alertable.
|
|
|
|
Timeout - Supplies a pointer to an optional absolute of relative time over
|
|
which the wait is to occur.
|
|
|
|
Return Value:
|
|
|
|
The wait completion status. A value of STATUS_TIMEOUT is returned if a
|
|
timeout occurred. The index of the object (zero based) in the object
|
|
pointer array is returned if an object satisfied the wait. A value of
|
|
STATUS_ALERTED is returned if the wait was aborted to deliver an alert
|
|
to the current thread. A value of STATUS_USER_APC is returned if the
|
|
wait was aborted to deliver a user APC to the current thread.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HANDLE CapturedHandles[MAXIMUM_WAIT_OBJECTS];
|
|
ULONG i;
|
|
ULONG j;
|
|
POBJECT_HEADER ObjectHeader;
|
|
PVOID Objects[MAXIMUM_WAIT_OBJECTS];
|
|
KPROCESSOR_MODE PreviousMode;
|
|
ULONG RefCount;
|
|
ULONG Size;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER TimeoutValue;
|
|
PKWAIT_BLOCK WaitBlockArray;
|
|
ACCESS_MASK GrantedAccess;
|
|
PVOID WaitObjects[MAXIMUM_WAIT_OBJECTS];
|
|
PHANDLE_TABLE HandleTable;
|
|
ULONG TableIndex;
|
|
PHANDLE_ENTRY HandleEntry;
|
|
PHANDLE_ENTRY TableBound;
|
|
PHANDLE_ENTRY TableEntries;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the number of objects is zero or greater than the largest number
|
|
// that can be waited on, then return and invalid parameter status.
|
|
//
|
|
|
|
if ((Count == 0) || (Count > MAXIMUM_WAIT_OBJECTS)) {
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
|
|
//
|
|
// If the wait type is not wait any or wait all, then return an invalid
|
|
// parameter status.
|
|
//
|
|
|
|
if ((WaitType != WaitAny) && (WaitType != WaitAll)) {
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
}
|
|
|
|
//
|
|
// Get previous processor mode and probe and capture input arguments if
|
|
// necessary.
|
|
//
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
try {
|
|
if (PreviousMode != KernelMode) {
|
|
if (ARGUMENT_PRESENT(Timeout)) {
|
|
TimeoutValue = ProbeAndReadLargeInteger(Timeout);
|
|
Timeout = &TimeoutValue;
|
|
}
|
|
|
|
ProbeForRead(Handles, Count * sizeof(HANDLE), sizeof(HANDLE));
|
|
}
|
|
|
|
i= 0;
|
|
do {
|
|
CapturedHandles[i] = (HANDLE)OBJ_HANDLE_TO_HANDLE_INDEX( Handles[i] );
|
|
i += 1;
|
|
}
|
|
while (i < Count);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// If the number of objects to be waited on is greater than the number
|
|
// of builtin wait blocks, then allocate an array of wait blocks from
|
|
// nonpaged pool. If the wait block array cannot be allocated, then
|
|
// return insufficient resources.
|
|
//
|
|
|
|
WaitBlockArray = NULL;
|
|
if (Count > THREAD_WAIT_OBJECTS) {
|
|
Size = Count * sizeof( KWAIT_BLOCK );
|
|
WaitBlockArray = ExAllocatePoolWithTag(NonPagedPool, Size, 'tiaW');
|
|
if (WaitBlockArray == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop through the array of handles and get a referenced pointer to
|
|
// each object.
|
|
//
|
|
|
|
//
|
|
// Get the address of the object table for the current process.
|
|
//
|
|
|
|
HandleTable = ObpGetObjectTable();
|
|
ExLockHandleTableShared( HandleTable );
|
|
TableBound = HandleTable->TableBound;
|
|
TableEntries = HandleTable->TableEntries;
|
|
|
|
i = 0;
|
|
RefCount = 0;
|
|
Status = STATUS_SUCCESS;
|
|
do {
|
|
|
|
//
|
|
// Get a referenced pointer to the specified objects with
|
|
// synchronize access.
|
|
//
|
|
|
|
TableIndex = HANDLE_TO_INDEX( CapturedHandles[ i ] );
|
|
if (TableIndex < (ULONG)(TableBound - TableEntries)) {
|
|
HandleEntry = &TableEntries[TableIndex];
|
|
if (ExIsEntryUsed(TableEntries, TableBound, HandleEntry)) {
|
|
#if i386 && !FPO
|
|
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
|
|
if (PreviousMode != KernelMode) {
|
|
GrantedAccess = ObpTranslateGrantedAccessIndex( ((POBJECT_TABLE_ENTRY)HandleEntry)->GrantedAccessIndex );
|
|
}
|
|
}
|
|
else
|
|
#endif // i386 && !FPO
|
|
GrantedAccess = (ACCESS_MASK)HandleEntry->Attributes;
|
|
if ((PreviousMode != KernelMode) &&
|
|
(SeComputeDeniedAccesses( GrantedAccess, SYNCHRONIZE ) != 0)) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
ExUnlockHandleTableShared( HandleTable );
|
|
goto ServiceFailed;
|
|
}
|
|
else {
|
|
ObjectHeader = (POBJECT_HEADER)((ULONG)HandleEntry->Object & ~OBJ_HANDLE_ATTRIBUTES);
|
|
|
|
if ((LONG)ObjectHeader->Type->DefaultObject < 0) {
|
|
RefCount += 1;
|
|
Objects[i] = NULL;
|
|
WaitObjects[i] = ObjectHeader->Type->DefaultObject;
|
|
}
|
|
else {
|
|
ObpIncrPointerCount( ObjectHeader );
|
|
RefCount += 1;
|
|
Objects[i] = &ObjectHeader->Body;
|
|
|
|
//
|
|
// Compute the address of the kernel wait object.
|
|
//
|
|
|
|
WaitObjects[i] = (PVOID)((PCHAR)&ObjectHeader->Body +
|
|
(ULONG)ObjectHeader->Type->DefaultObject
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
ExUnlockHandleTableShared( HandleTable );
|
|
goto ServiceFailed;
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
ExUnlockHandleTableShared( HandleTable );
|
|
goto ServiceFailed;
|
|
}
|
|
|
|
i += 1;
|
|
}
|
|
while (i < Count);
|
|
|
|
ExUnlockHandleTableShared( HandleTable );
|
|
|
|
//
|
|
// Check to determine if any of the objects are specified more than once.
|
|
//
|
|
|
|
if (WaitType == WaitAll) {
|
|
i = 0;
|
|
do {
|
|
for (j = i + 1; j < Count; j += 1) {
|
|
if (WaitObjects[i] == WaitObjects[j]) {
|
|
Status = STATUS_INVALID_PARAMETER_MIX;
|
|
goto ServiceFailed;
|
|
}
|
|
}
|
|
|
|
i += 1;
|
|
}
|
|
while (i < Count);
|
|
}
|
|
|
|
//
|
|
// Wait for the specified objects to attain a state of Signaled or a
|
|
// time out to occur.
|
|
//
|
|
|
|
Status = KeWaitForMultipleObjects(Count,
|
|
WaitObjects,
|
|
WaitType,
|
|
UserRequest,
|
|
PreviousMode,
|
|
Alertable,
|
|
Timeout,
|
|
WaitBlockArray);
|
|
|
|
//
|
|
// If any objects were referenced, then deference them.
|
|
//
|
|
|
|
ServiceFailed:
|
|
while (RefCount > 0) {
|
|
RefCount -= 1;
|
|
if (Objects[RefCount] != NULL) {
|
|
ObDereferenceObject(Objects[RefCount]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a wait block array was allocated, then deallocate it.
|
|
//
|
|
|
|
if (WaitBlockArray != NULL) {
|
|
ExFreePool(WaitBlockArray);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ObWaitForSingleObject (
|
|
IN HANDLE Handle,
|
|
IN BOOLEAN Alertable,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|
)
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
PVOID Object;
|
|
NTSTATUS Status;
|
|
PVOID WaitObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a referenced pointer to the specified object with synchronize
|
|
// access.
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle( Handle,
|
|
SYNCHRONIZE,
|
|
(POBJECT_TYPE)NULL,
|
|
KernelMode,
|
|
&Object,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// If access is granted, then check to determine if the specified object
|
|
// can be waited on.
|
|
//
|
|
|
|
if (NT_SUCCESS( Status ) != FALSE) {
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
if ((LONG)ObjectHeader->Type->DefaultObject < 0) {
|
|
WaitObject = (PVOID)ObjectHeader->Type->DefaultObject;
|
|
}
|
|
else {
|
|
WaitObject = (PVOID)((PCHAR)Object + (ULONG)ObjectHeader->Type->DefaultObject);
|
|
}
|
|
|
|
Status = KeWaitForSingleObject( WaitObject,
|
|
UserRequest,
|
|
KernelMode,
|
|
Alertable,
|
|
Timeout
|
|
);
|
|
|
|
ObDereferenceObject(Object);
|
|
}
|
|
|
|
return Status;
|
|
}
|