[KMTESTS]
authorThomas Faber <thomas.faber@reactos.org>
Fri, 5 Aug 2011 21:12:11 +0000 (21:12 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Fri, 5 Aug 2011 21:12:11 +0000 (21:12 +0000)
- KeEvent: a little concurrent testing (part 2/x)
- ExFastMutex: concurrent testing (part 2/2)
- Fix copypasta

svn path=/branches/GSoC_2011/KMTestSuite/; revision=53091

kmtests/include/kmt_platform.h
kmtests/ntos_ex/ExFastMutex.c
kmtests/ntos_ke/KeEvent.c

index ca79a8c..a13a9cf 100644 (file)
@@ -41,7 +41,7 @@ typedef ULONG LOGICAL, *PLOGICAL;
 #define ExAllocatePoolWithTag(type, size, tag)  HeapAlloc(GetProcessHeap(), 0, size)
 #define ExFreePool(p)                           HeapFree(GetProcessHeap(), 0, p)
 #define ExFreePoolWithTag(p, tag)               HeapFree(GetProcessHeap(), 0, p)
-#define RtlCopyMemoryNonTemporal(d, s, l)
+#define RtlCopyMemoryNonTemporal                RtlCopyMemory
 #define RtlPrefetchMemoryNonTemporal(s, l)
 #endif /* defined KMT_EMULATE_KERNEL */
 
index ab013c3..53ec7fd 100644 (file)
@@ -104,6 +104,180 @@ TestFastMutex(
     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;
@@ -124,4 +298,6 @@ START_TEST(ExFastMutex)
         TestFastMutex(&Mutex, HIGH_LEVEL);
     }
     KeLowerIrql(PASSIVE_LEVEL);
+
+    TestFastMutexConcurrent(&Mutex);
 }
index a5cdcb7..2b00e3a 100644 (file)
@@ -7,7 +7,7 @@
 
 #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       \
 {                                                                               \
@@ -96,6 +96,95 @@ TestEventFunctional(
     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;
@@ -111,6 +200,8 @@ START_TEST(KeEvent)
         KeLowerIrql(Irql);
     }
 
+    TestEventThreads(&Event, NotificationEvent, PASSIVE_LEVEL);
+
     ok_irql(PASSIVE_LEVEL);
     KmtSetIrql(PASSIVE_LEVEL);
 }