[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ex / keyedevt.c
index 1d409ba..d836b76 100644 (file)
@@ -3,7 +3,7 @@
  * PROJECT:         ReactOS Kernel
  * FILE:            ntoskrnl/ex/keyedevt.c
  * PURPOSE:         Support for keyed events
- * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+ * PROGRAMMERS:     Timo Kreuzer (timo.kreuzer@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 #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;
+
+NTSTATUS
+NTAPI
+ZwCreateKeyedEvent(
+    _Out_ PHANDLE OutHandle,
+    _In_ ACCESS_MASK AccessMask,
+    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
+    _In_ ULONG Flags);
+
+#define KeGetCurrentProcess() ((PKPROCESS)PsGetCurrentProcess())
+
 /* GLOBALS *******************************************************************/
 
+PEX_KEYED_EVENT ExpCritSecOutOfMemoryEvent;
+POBJECT_TYPE ExKeyedEventObjectType;
+
+static
+GENERIC_MAPPING ExpKeyedEventMapping =
+{
+    STANDARD_RIGHTS_READ | EVENT_QUERY_STATE,
+    STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE,
+    STANDARD_RIGHTS_EXECUTE,
+    EVENT_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 = EVENT_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,
+                                EVENT_ALL_ACCESS,
+                                &ObjectAttributes,
+                                0);
+    if (NT_SUCCESS(Status))
+    {
+        /* Take a reference so we can get rid of the handle */
+        Status = ObReferenceObjectByHandle(EventHandle,
+                                           EVENT_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
-NtCreateKeyedEvent(OUT PHANDLE KeyedEventHandle,
-                   IN ACCESS_MASK DesiredAccess,
-                   IN POBJECT_ATTRIBUTES ObjectAttributes,
-                   IN ULONG Flags)
+ExpReleaseOrWaitForKeyedEvent(
+    _Inout_ PEX_KEYED_EVENT KeyedEvent,
+    _In_ PVOID KeyedWaitValue,
+    _In_ BOOLEAN Alertable,
+    _In_ PLARGE_INTEGER Timeout,
+    _In_ BOOLEAN Release)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PETHREAD Thread, CurrentThread;
+    PKPROCESS CurrentProcess;
+    PLIST_ENTRY ListEntry, WaitListHead1, WaitListHead2;
+    NTSTATUS Status;
+    ULONG_PTR HashIndex;
+
+    /* Get the current process */
+    CurrentProcess = KeGetCurrentProcess();
+
+    /* Calculate the hash index */
+    HashIndex = (ULONG_PTR)KeyedWaitValue >> 5;
+    HashIndex ^= (ULONG_PTR)CurrentProcess >> 6;
+    HashIndex %= NUM_KEY_HASH_BUCKETS;
+
+    /* Lock the lists */
+    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)
+    {
+        Thread = CONTAINING_RECORD(ListEntry, ETHREAD, KeyedWaitChain);
+
+        /* Check if this thread is a correct waiter */
+        if ((Thread->Tcb.Process == CurrentProcess) &&
+            (Thread->KeyedWaitValue == KeyedWaitValue))
+        {
+            /* Remove the thread from the list */
+            RemoveEntryList(&Thread->KeyedWaitChain);
+
+            /* Wake the thread */
+            KeReleaseSemaphore(&Thread->KeyedWaitSemaphore, 0, 1, FALSE);
+
+            /* Unlock the lists */
+            ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
+
+            return STATUS_SUCCESS;
+        }
+    }
+
+    /* Get the current thread */
+    CurrentThread = PsGetCurrentThread();
+
+    /* Set the wait key */
+    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 lists */
+    ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
+
+    /* Wait for the keyed wait semaphore */
+    Status = KeWaitForSingleObject(&CurrentThread->KeyedWaitSemaphore,
+                                   WrKeyedEvent,
+                                   KernelMode,
+                                   Alertable,
+                                   Timeout);
+
+    return STATUS_SUCCESS;
 }
 
+_IRQL_requires_max_(APC_LEVEL)
 NTSTATUS
 NTAPI
-NtOpenKeyedEvent(OUT PHANDLE EventHandle,
-                 IN ACCESS_MASK DesiredAccess,
-                 IN POBJECT_ATTRIBUTES ObjectAttributes)
+ExpWaitForKeyedEvent(
+    _Inout_ PEX_KEYED_EVENT KeyedEvent,
+    _In_ PVOID KeyedWaitValue,
+    _In_ BOOLEAN Alertable,
+    _In_ PLARGE_INTEGER Timeout)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    /* Call the generic internal function */
+    return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
+                                         KeyedWaitValue,
+                                         Alertable,
+                                         Timeout,
+                                         FALSE);
 }
 
+_IRQL_requires_max_(APC_LEVEL)
 NTSTATUS
 NTAPI
-NtReleaseKeyedEvent(IN HANDLE EventHandle,
-                    IN PVOID Key,
-                    IN BOOLEAN Alertable,
-                    IN PLARGE_INTEGER Timeout OPTIONAL)
+ExpReleaseKeyedEvent(
+    _Inout_ PEX_KEYED_EVENT KeyedEvent,
+    _In_ PVOID KeyedWaitValue,
+       _In_ BOOLEAN Alertable,
+       _In_ PLARGE_INTEGER Timeout)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    /* Call the generic internal function */
+    return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
+                                         KeyedWaitValue,
+                                         Alertable,
+                                         Timeout,
+                                         TRUE);
 }
 
+_IRQL_requires_max_(APC_LEVEL)
 NTSTATUS
 NTAPI
-NtWaitForKeyedEvent(IN HANDLE EventHandle,
-                    IN PVOID Key,
-                    IN BOOLEAN Alertable,
-                    IN PLARGE_INTEGER Timeout OPTIONAL)
+NtCreateKeyedEvent(
+    _Out_ PHANDLE OutHandle,
+    _In_ ACCESS_MASK AccessMask,
+    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
+    _In_ ULONG Flags)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    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;
+
+    /* Initalize 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_(APC_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();
+        }
+        _SEH2_END;
+    }
+    else
+    {
+        *OutHandle = KeyedEventHandle;
+    }
+
+    /* Return status */
+    return Status;
+}
+
+_IRQL_requires_max_(APC_LEVEL)
+NTSTATUS
+NTAPI
+NtWaitForKeyedEvent(
+    _In_ HANDLE Handle,
+    _In_ PVOID Key,
+    _In_ BOOLEAN Alertable,
+    _In_ PLARGE_INTEGER Timeout)
+{
+    KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
+    PEX_KEYED_EVENT KeyedEvent;
+    NTSTATUS Status;
+
+    /* Check if the caller provided a handle */
+    if (Handle != NULL)
+    {
+        /* Get the keyed event object */
+        Status = ObReferenceObjectByHandle(Handle,
+                                           EVENT_MODIFY_STATE,
+                                           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_(APC_LEVEL)
+NTSTATUS
+NTAPI
+NtReleaseKeyedEvent(
+    _In_ HANDLE Handle,
+    _In_ PVOID Key,
+    _In_ BOOLEAN Alertable,
+    _In_ PLARGE_INTEGER Timeout)
+{
+    KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
+    PEX_KEYED_EVENT KeyedEvent;
+    NTSTATUS Status;
+
+    /* Check if the caller provided a handle */
+    if (Handle != NULL)
+    {
+        /* Get the keyed event object */
+        Status = ObReferenceObjectByHandle(Handle,
+                                           EVENT_MODIFY_STATE,
+                                           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 */