mirror of
https://github.com/reactos/reactos.git
synced 2024-08-31 07:38:48 +00:00
534 lines
16 KiB
C
534 lines
16 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Kernel
|
|
* FILE: ntoskrnl/ex/keyedevt.c
|
|
* PURPOSE: Support for keyed events
|
|
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* INTERNAL TYPES *************************************************************/
|
|
|
|
#define NUM_KEY_HASH_BUCKETS 23
|
|
typedef struct _EX_KEYED_EVENT
|
|
{
|
|
struct
|
|
{
|
|
EX_PUSH_LOCK Lock;
|
|
LIST_ENTRY WaitListHead;
|
|
LIST_ENTRY ReleaseListHead;
|
|
} HashTable[NUM_KEY_HASH_BUCKETS];
|
|
} EX_KEYED_EVENT, *PEX_KEYED_EVENT;
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
PEX_KEYED_EVENT ExpCritSecOutOfMemoryEvent;
|
|
POBJECT_TYPE ExKeyedEventObjectType;
|
|
|
|
static
|
|
GENERIC_MAPPING ExpKeyedEventMapping =
|
|
{
|
|
STANDARD_RIGHTS_READ | KEYEDEVENT_WAIT,
|
|
STANDARD_RIGHTS_WRITE | KEYEDEVENT_WAKE,
|
|
STANDARD_RIGHTS_EXECUTE,
|
|
KEYEDEVENT_ALL_ACCESS
|
|
};
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
BOOLEAN
|
|
INIT_FUNCTION
|
|
NTAPI
|
|
ExpInitializeKeyedEventImplementation(VOID)
|
|
{
|
|
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer = {0};
|
|
UNICODE_STRING TypeName = RTL_CONSTANT_STRING(L"KeyedEvent");
|
|
UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\KernelObjects\\CritSecOutOfMemoryEvent");
|
|
NTSTATUS Status;
|
|
HANDLE EventHandle;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
/* Set up the object type initializer */
|
|
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
|
|
ObjectTypeInitializer.GenericMapping = ExpKeyedEventMapping;
|
|
ObjectTypeInitializer.PoolType = PagedPool;
|
|
ObjectTypeInitializer.ValidAccessMask = KEYEDEVENT_ALL_ACCESS;
|
|
ObjectTypeInitializer.UseDefaultObject = TRUE;
|
|
|
|
/* Create the keyed event object type */
|
|
Status = ObCreateObjectType(&TypeName,
|
|
&ObjectTypeInitializer,
|
|
NULL,
|
|
&ExKeyedEventObjectType);
|
|
if (!NT_SUCCESS(Status)) return FALSE;
|
|
|
|
/* Create the out of memory event for critical sections */
|
|
InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_PERMANENT, NULL, NULL);
|
|
Status = ZwCreateKeyedEvent(&EventHandle,
|
|
KEYEDEVENT_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
0);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Take a reference so we can get rid of the handle */
|
|
Status = ObReferenceObjectByHandle(EventHandle,
|
|
KEYEDEVENT_ALL_ACCESS,
|
|
ExKeyedEventObjectType,
|
|
KernelMode,
|
|
(PVOID*)&ExpCritSecOutOfMemoryEvent,
|
|
NULL);
|
|
ZwClose(EventHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ExpInitializeKeyedEvent(
|
|
_Out_ PEX_KEYED_EVENT KeyedEvent)
|
|
{
|
|
ULONG i;
|
|
|
|
/* Loop all hash buckets */
|
|
for (i = 0; i < NUM_KEY_HASH_BUCKETS; i++)
|
|
{
|
|
/* Initialize the mutex and the wait lists */
|
|
ExInitializePushLock(&KeyedEvent->HashTable[i].Lock);
|
|
InitializeListHead(&KeyedEvent->HashTable[i].WaitListHead);
|
|
InitializeListHead(&KeyedEvent->HashTable[i].ReleaseListHead);
|
|
}
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
NTAPI
|
|
ExpReleaseOrWaitForKeyedEvent(
|
|
_Inout_ PEX_KEYED_EVENT KeyedEvent,
|
|
_In_ PVOID KeyedWaitValue,
|
|
_In_ BOOLEAN Alertable,
|
|
_In_ PLARGE_INTEGER Timeout,
|
|
_In_ BOOLEAN Release)
|
|
{
|
|
PETHREAD Thread, CurrentThread;
|
|
PEPROCESS CurrentProcess;
|
|
PLIST_ENTRY ListEntry, WaitListHead1, WaitListHead2;
|
|
NTSTATUS Status;
|
|
ULONG_PTR HashIndex;
|
|
PVOID PreviousKeyedWaitValue;
|
|
|
|
/* Get the current process */
|
|
CurrentProcess = PsGetCurrentProcess();
|
|
|
|
/* Calculate the hash index */
|
|
HashIndex = (ULONG_PTR)KeyedWaitValue >> 5;
|
|
HashIndex ^= (ULONG_PTR)CurrentProcess >> 6;
|
|
HashIndex %= NUM_KEY_HASH_BUCKETS;
|
|
|
|
/* Lock the lists */
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
|
|
|
|
/* Get the lists for search and wait, depending on whether
|
|
we want to wait for the event or signal it */
|
|
if (Release)
|
|
{
|
|
WaitListHead1 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
|
|
WaitListHead2 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
|
|
}
|
|
else
|
|
{
|
|
WaitListHead1 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
|
|
WaitListHead2 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
|
|
}
|
|
|
|
/* loop the first wait list */
|
|
ListEntry = WaitListHead1->Flink;
|
|
while (ListEntry != WaitListHead1)
|
|
{
|
|
/* Get the waiting thread. Note that this thread cannot be terminated
|
|
as long as we hold the list lock, since it either needs to wait to
|
|
be signaled by this thread or, when the wait is aborted due to thread
|
|
termination, then it first needs to acquire the list lock. */
|
|
Thread = CONTAINING_RECORD(ListEntry, ETHREAD, KeyedWaitChain);
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
/* Check if this thread is a correct waiter */
|
|
if ((Thread->Tcb.Process == &CurrentProcess->Pcb) &&
|
|
(Thread->KeyedWaitValue == KeyedWaitValue))
|
|
{
|
|
/* Remove the thread from the list */
|
|
RemoveEntryList(&Thread->KeyedWaitChain);
|
|
|
|
/* Initialize the list entry to show that it was removed */
|
|
InitializeListHead(&Thread->KeyedWaitChain);
|
|
|
|
/* Wake the thread */
|
|
KeReleaseSemaphore(&Thread->KeyedWaitSemaphore,
|
|
IO_NO_INCREMENT,
|
|
1,
|
|
FALSE);
|
|
Thread = NULL;
|
|
|
|
/* Unlock the list. After this it is not safe to access Thread */
|
|
ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
|
|
KeLeaveCriticalRegion();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Get the current thread */
|
|
CurrentThread = PsGetCurrentThread();
|
|
|
|
/* Set the wait key and remember the old value */
|
|
PreviousKeyedWaitValue = CurrentThread->KeyedWaitValue;
|
|
CurrentThread->KeyedWaitValue = KeyedWaitValue;
|
|
|
|
/* Initialize the wait semaphore */
|
|
KeInitializeSemaphore(&CurrentThread->KeyedWaitSemaphore, 0, 1);
|
|
|
|
/* Insert the current thread into the secondary wait list */
|
|
InsertTailList(WaitListHead2, &CurrentThread->KeyedWaitChain);
|
|
|
|
/* Unlock the list */
|
|
ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
|
|
KeLeaveCriticalRegion();
|
|
|
|
/* Wait for the keyed wait semaphore */
|
|
Status = KeWaitForSingleObject(&CurrentThread->KeyedWaitSemaphore,
|
|
WrKeyedEvent,
|
|
KernelMode,
|
|
Alertable,
|
|
Timeout);
|
|
|
|
/* Check if the wait was aborted or timed out */
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
/* Lock the lists to make sure no one else messes with the entry */
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
|
|
|
|
/* Check if the wait list entry is still in the list */
|
|
if (!IsListEmpty(&CurrentThread->KeyedWaitChain))
|
|
{
|
|
/* Remove the thread from the list */
|
|
RemoveEntryList(&CurrentThread->KeyedWaitChain);
|
|
InitializeListHead(&CurrentThread->KeyedWaitChain);
|
|
}
|
|
|
|
/* Unlock the list */
|
|
ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
/* Restore the previous KeyedWaitValue, since this is a union member */
|
|
CurrentThread->KeyedWaitValue = PreviousKeyedWaitValue;
|
|
|
|
return Status;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
NTAPI
|
|
ExpWaitForKeyedEvent(
|
|
_Inout_ PEX_KEYED_EVENT KeyedEvent,
|
|
_In_ PVOID KeyedWaitValue,
|
|
_In_ BOOLEAN Alertable,
|
|
_In_ PLARGE_INTEGER Timeout)
|
|
{
|
|
/* Call the generic internal function */
|
|
return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
|
|
KeyedWaitValue,
|
|
Alertable,
|
|
Timeout,
|
|
FALSE);
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
NTAPI
|
|
ExpReleaseKeyedEvent(
|
|
_Inout_ PEX_KEYED_EVENT KeyedEvent,
|
|
_In_ PVOID KeyedWaitValue,
|
|
_In_ BOOLEAN Alertable,
|
|
_In_ PLARGE_INTEGER Timeout)
|
|
{
|
|
/* Call the generic internal function */
|
|
return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
|
|
KeyedWaitValue,
|
|
Alertable,
|
|
Timeout,
|
|
TRUE);
|
|
}
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCreateKeyedEvent(
|
|
_Out_ PHANDLE OutHandle,
|
|
_In_ ACCESS_MASK AccessMask,
|
|
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
|
|
_In_ ULONG Flags)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
PEX_KEYED_EVENT KeyedEvent;
|
|
HANDLE KeyedEventHandle;
|
|
NTSTATUS Status;
|
|
|
|
/* Check flags */
|
|
if (Flags != 0)
|
|
{
|
|
/* We don't support any flags yet */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Create the object */
|
|
Status = ObCreateObject(PreviousMode,
|
|
ExKeyedEventObjectType,
|
|
ObjectAttributes,
|
|
PreviousMode,
|
|
NULL,
|
|
sizeof(EX_KEYED_EVENT),
|
|
0,
|
|
0,
|
|
(PVOID*)&KeyedEvent);
|
|
|
|
/* Check for success */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Initialize the keyed event */
|
|
ExpInitializeKeyedEvent(KeyedEvent);
|
|
|
|
/* Insert it */
|
|
Status = ObInsertObject(KeyedEvent,
|
|
NULL,
|
|
AccessMask,
|
|
0,
|
|
NULL,
|
|
&KeyedEventHandle);
|
|
|
|
/* Check for success (ObInsertObject dereferences!) */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Enter SEH for return */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the handle to the caller */
|
|
ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
|
|
*OutHandle = KeyedEventHandle;
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
/* Cleanup */
|
|
ObCloseHandle(KeyedEventHandle, PreviousMode);
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
*OutHandle = KeyedEventHandle;
|
|
}
|
|
|
|
/* Return Status */
|
|
return Status;
|
|
}
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
NTAPI
|
|
NtOpenKeyedEvent(
|
|
_Out_ PHANDLE OutHandle,
|
|
_In_ ACCESS_MASK AccessMask,
|
|
_In_ POBJECT_ATTRIBUTES ObjectAttributes)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
HANDLE KeyedEventHandle;
|
|
NTSTATUS Status;
|
|
|
|
/* Open the object */
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
ExKeyedEventObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
AccessMask,
|
|
NULL,
|
|
&KeyedEventHandle);
|
|
|
|
/* Check for success */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Enter SEH for return */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the handle to the caller */
|
|
ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
|
|
*OutHandle = KeyedEventHandle;
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
/* Cleanup */
|
|
ObCloseHandle(KeyedEventHandle, PreviousMode);
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
*OutHandle = KeyedEventHandle;
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
NTAPI
|
|
NtWaitForKeyedEvent(
|
|
_In_opt_ HANDLE Handle,
|
|
_In_ PVOID Key,
|
|
_In_ BOOLEAN Alertable,
|
|
_In_opt_ PLARGE_INTEGER Timeout)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
PEX_KEYED_EVENT KeyedEvent;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER TimeoutCopy;
|
|
|
|
/* Key must always be two-byte aligned */
|
|
if ((ULONG_PTR)Key & 1)
|
|
{
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
|
|
/* Check if the caller passed a timeout value and this is from user mode */
|
|
if ((Timeout != NULL) && (PreviousMode != KernelMode))
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(Timeout, sizeof(*Timeout), 1);
|
|
TimeoutCopy = *Timeout;
|
|
Timeout = &TimeoutCopy;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Check if the caller provided a handle */
|
|
if (Handle != NULL)
|
|
{
|
|
/* Get the keyed event object */
|
|
Status = ObReferenceObjectByHandle(Handle,
|
|
KEYEDEVENT_WAIT,
|
|
ExKeyedEventObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyedEvent,
|
|
NULL);
|
|
|
|
/* Check for success */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
}
|
|
else
|
|
{
|
|
/* Use the default keyed event for low memory critical sections */
|
|
KeyedEvent = ExpCritSecOutOfMemoryEvent;
|
|
}
|
|
|
|
/* Do the wait */
|
|
Status = ExpWaitForKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
|
|
|
|
/* Dereference the keyed event */
|
|
ObDereferenceObject(KeyedEvent);
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
NTAPI
|
|
NtReleaseKeyedEvent(
|
|
_In_opt_ HANDLE Handle,
|
|
_In_ PVOID Key,
|
|
_In_ BOOLEAN Alertable,
|
|
_In_opt_ PLARGE_INTEGER Timeout)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
PEX_KEYED_EVENT KeyedEvent;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER TimeoutCopy;
|
|
|
|
/* Key must always be two-byte aligned */
|
|
if ((ULONG_PTR)Key & 1)
|
|
{
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
|
|
/* Check if the caller passed a timeout value and this is from user mode */
|
|
if ((Timeout != NULL) && (PreviousMode != KernelMode))
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(Timeout, sizeof(*Timeout), 1);
|
|
TimeoutCopy = *Timeout;
|
|
Timeout = &TimeoutCopy;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Check if the caller provided a handle */
|
|
if (Handle != NULL)
|
|
{
|
|
/* Get the keyed event object */
|
|
Status = ObReferenceObjectByHandle(Handle,
|
|
KEYEDEVENT_WAKE,
|
|
ExKeyedEventObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyedEvent,
|
|
NULL);
|
|
|
|
/* Check for success */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
}
|
|
else
|
|
{
|
|
/* Use the default keyed event for low memory critical sections */
|
|
KeyedEvent = ExpCritSecOutOfMemoryEvent;
|
|
}
|
|
|
|
/* Do the wait */
|
|
Status = ExpReleaseKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
|
|
|
|
/* Dereference the keyed event */
|
|
ObDereferenceObject(KeyedEvent);
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|