+++ /dev/null
-/*
- * 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 */