reactos/ntoskrnl/ex/keyedevt.c
Hermès Bélusca-Maïto f44e914e80 Sync with trunk r58033.
svn path=/branches/ros-csrss/; revision=58034
2012-12-28 23:37:33 +00:00

455 lines
13 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;
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
ExpReleaseOrWaitForKeyedEvent(
_Inout_ PEX_KEYED_EVENT KeyedEvent,
_In_ PVOID KeyedWaitValue,
_In_ BOOLEAN Alertable,
_In_ PLARGE_INTEGER Timeout,
_In_ BOOLEAN Release)
{
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
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_(APC_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;
/* 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 */