KmtSetIrql(OriginalIrql);
}
+typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PFAST_MUTEX);
+typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PFAST_MUTEX);
+
+typedef struct
+{
+ HANDLE Handle;
+ PKTHREAD Thread;
+ KIRQL Irql;
+ PFAST_MUTEX Mutex;
+ PMUTEX_FUNCTION Acquire;
+ PMUTEX_TRY_FUNCTION TryAcquire;
+ PMUTEX_FUNCTION Release;
+ BOOLEAN Try;
+ BOOLEAN RetExpected;
+ KEVENT InEvent;
+ KEVENT OutEvent;
+} THREAD_DATA, *PTHREAD_DATA;
+
+static
+VOID
+NTAPI
+AcquireMutexThread(
+ PVOID Parameter)
+{
+ PTHREAD_DATA ThreadData = Parameter;
+ KIRQL Irql;
+ BOOLEAN Ret = FALSE;
+ NTSTATUS Status;
+
+ KeRaiseIrql(ThreadData->Irql, &Irql);
+
+ if (ThreadData->Try)
+ {
+ Ret = ThreadData->TryAcquire(ThreadData->Mutex);
+ ok_eq_bool(Ret, ThreadData->RetExpected);
+ }
+ else
+ ThreadData->Acquire(ThreadData->Mutex);
+
+ ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
+ Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ if (!ThreadData->Try || Ret)
+ ThreadData->Release(ThreadData->Mutex);
+
+ KeLowerIrql(Irql);
+}
+
+static
+VOID
+InitThreadData(
+ PTHREAD_DATA ThreadData,
+ PFAST_MUTEX Mutex,
+ PMUTEX_FUNCTION Acquire,
+ PMUTEX_TRY_FUNCTION TryAcquire,
+ PMUTEX_FUNCTION Release)
+{
+ ThreadData->Mutex = Mutex;
+ KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
+ KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
+ ThreadData->Acquire = Acquire;
+ ThreadData->TryAcquire = TryAcquire;
+ ThreadData->Release = Release;
+}
+
+static
+NTSTATUS
+StartThread(
+ PTHREAD_DATA ThreadData,
+ PLARGE_INTEGER Timeout,
+ KIRQL Irql,
+ BOOLEAN Try,
+ BOOLEAN RetExpected)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES Attributes;
+
+ ThreadData->Try = Try;
+ ThreadData->Irql = Irql;
+ ThreadData->RetExpected = RetExpected;
+ InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+ Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireMutexThread, ThreadData);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
+}
+
+static
+VOID
+FinishThread(
+ PTHREAD_DATA ThreadData)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ KeSetEvent(&ThreadData->InEvent, 0, TRUE);
+ Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ ObDereferenceObject(ThreadData->Thread);
+ Status = ZwClose(ThreadData->Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ KeClearEvent(&ThreadData->InEvent);
+ KeClearEvent(&ThreadData->OutEvent);
+}
+
+static
+VOID
+TestFastMutexConcurrent(
+ PFAST_MUTEX Mutex)
+{
+ NTSTATUS Status;
+ THREAD_DATA ThreadData;
+ THREAD_DATA ThreadData2;
+ THREAD_DATA ThreadDataUnsafe;
+ THREAD_DATA ThreadDataTry;
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
+
+ InitThreadData(&ThreadData, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
+ InitThreadData(&ThreadData2, Mutex, ExAcquireFastMutex, NULL, ExReleaseFastMutex);
+ InitThreadData(&ThreadDataUnsafe, Mutex, ExAcquireFastMutexUnsafe, NULL, ExReleaseFastMutexUnsafe);
+ InitThreadData(&ThreadDataTry, Mutex, NULL, ExTryToAcquireFastMutex, ExReleaseFastMutex);
+
+ /* have a thread acquire the mutex */
+ Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+ /* have a second thread try to acquire it -- should fail */
+ Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+ FinishThread(&ThreadDataTry);
+
+ /* have another thread acquire it -- should block */
+ Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, -1L, ThreadData.Thread, 1LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+
+ /* finish the first thread -- now the second should become available */
+ FinishThread(&ThreadData);
+ Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ /* block two more threads */
+ Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, -1L, ThreadData2.Thread, 2LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ CheckMutex(Mutex, -2L, ThreadData2.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ /* finish 1 */
+ FinishThread(&ThreadData2);
+ Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, -1L, ThreadDataUnsafe.Thread, 3LU, APC_LEVEL, PASSIVE_LEVEL);
+
+ /* finish 2 */
+ FinishThread(&ThreadDataUnsafe);
+ Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+
+ /* finish 3 */
+ FinishThread(&ThreadData);
+
+ CheckMutex(Mutex, 1L, NULL, 3LU, PASSIVE_LEVEL, PASSIVE_LEVEL);
+}
+
START_TEST(ExFastMutex)
{
FAST_MUTEX Mutex;
TestFastMutex(&Mutex, HIGH_LEVEL);
}
KeLowerIrql(PASSIVE_LEVEL);
+
+ TestFastMutexConcurrent(&Mutex);
}
#include <kmt_test.h>
-/* TODO: thread testing, exports vs macros */
+/* TODO: more thread testing, exports vs macros */
#define CheckEvent(Event, ExpectedType, State, ExpectedWaitNext, Irql) do \
{ \
KmtSetIrql(OriginalIrql);
}
+typedef struct
+{
+ HANDLE Handle;
+ PKTHREAD Thread;
+ PKEVENT Event1;
+ PKEVENT Event2;
+ volatile BOOLEAN Signal;
+} THREAD_DATA, *PTHREAD_DATA;
+
+static
+VOID
+NTAPI
+WaitForEventThread(
+ IN OUT PVOID Context)
+{
+ NTSTATUS Status;
+ PTHREAD_DATA ThreadData = Context;
+
+ ok_irql(PASSIVE_LEVEL);
+ ThreadData->Signal = TRUE;
+ Status = KeWaitForSingleObject(ThreadData->Event1, Executive, KernelMode, FALSE, NULL);
+ ok_irql(PASSIVE_LEVEL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ThreadData->Signal = TRUE;
+ Status = KeWaitForSingleObject(ThreadData->Event2, Executive, KernelMode, FALSE, NULL);
+ ok_irql(PASSIVE_LEVEL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ ok_irql(PASSIVE_LEVEL);
+}
+
+static
+VOID
+TestEventThreads(
+ IN PKEVENT Event,
+ IN EVENT_TYPE Type,
+ IN KIRQL OriginalIrql)
+{
+ NTSTATUS Status;
+ THREAD_DATA Threads[5];
+ LARGE_INTEGER Timeout;
+ KPRIORITY Priority;
+ KEVENT WaitEvent;
+ KEVENT TerminateEvent;
+ int i;
+ Timeout.QuadPart = -1000 * 10;
+
+ KeInitializeEvent(Event, Type, FALSE);
+ KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE);
+ KeInitializeEvent(&TerminateEvent, SynchronizationEvent, FALSE);
+
+ for (i = 0; i < sizeof Threads / sizeof Threads[0]; ++i)
+ {
+ Threads[i].Event1 = Event;
+ Threads[i].Event2 = &TerminateEvent;
+ Threads[i].Signal = FALSE;
+ Status = PsCreateSystemThread(&Threads[i].Handle, GENERIC_ALL, NULL, NULL, NULL, WaitForEventThread, &Threads[i]);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ Status = ObReferenceObjectByHandle(Threads[i].Handle, SYNCHRONIZE, PsThreadType, KernelMode, (PVOID *)&Threads[i].Thread, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ Priority = KeQueryPriorityThread(Threads[i].Thread);
+ ok_eq_long(Priority, 8L);
+ while (!Threads[i].Signal)
+ {
+ Status = KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, &Timeout);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ }
+ Threads[i].Signal = FALSE;
+ }
+
+ for (i = 0; i < sizeof Threads / sizeof Threads[0]; ++i)
+ {
+ KeSetEvent(Event, 1, FALSE);
+ while (!Threads[i].Signal)
+ {
+ Status = KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, &Timeout);
+ ok_eq_hex(Status, STATUS_TIMEOUT);
+ }
+ Priority = KeQueryPriorityThread(Threads[i].Thread);
+ ok_eq_long(Priority, 9L);
+ KeSetEvent(&TerminateEvent, 0, FALSE);
+ Status = KeWaitForSingleObject(Threads[i].Thread, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+
+ ObDereferenceObject(Threads[i].Thread);
+ Status = ZwClose(Threads[i].Handle);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
+}
+
START_TEST(KeEvent)
{
KEVENT Event;
KeLowerIrql(Irql);
}
+ TestEventThreads(&Event, NotificationEvent, PASSIVE_LEVEL);
+
ok_irql(PASSIVE_LEVEL);
KmtSetIrql(PASSIVE_LEVEL);
}