[KMTESTS/KE]
authorThomas Faber <thomas.faber@reactos.org>
Mon, 22 Aug 2011 10:02:12 +0000 (10:02 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Mon, 22 Aug 2011 10:02:12 +0000 (10:02 +0000)
- Add concurrent testing to KeGuardedMutex test (part 2/2)

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

kmtests/ntos_ke/KeGuardedMutex.c

index ecbe50b..6f4101e 100644 (file)
@@ -116,6 +116,185 @@ TestGuardedMutex(
     ok_irql(OriginalIrql);
 }
 
+typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PKGUARDED_MUTEX);
+typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PKGUARDED_MUTEX);
+
+typedef struct
+{
+    HANDLE Handle;
+    PKTHREAD Thread;
+    KIRQL Irql;
+    PKGUARDED_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;
+
+    DPRINT("Thread starting\n");
+    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");
+    DPRINT("Thread now waiting\n");
+    Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
+    DPRINT("Thread done waiting\n");
+    ok_eq_hex(Status, STATUS_SUCCESS);
+
+    if (!ThreadData->Try || Ret)
+        ThreadData->Release(ThreadData->Mutex);
+
+    KeLowerIrql(Irql);
+    DPRINT("Thread exiting\n");
+}
+
+static
+VOID
+InitThreadData(
+    PTHREAD_DATA ThreadData,
+    PKGUARDED_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
+TestGuardedMutexConcurrent(
+    PKGUARDED_MUTEX Mutex)
+{
+    NTSTATUS Status;
+    THREAD_DATA ThreadData;
+    THREAD_DATA ThreadData2;
+    THREAD_DATA ThreadDataUnsafe;
+    THREAD_DATA ThreadDataTry;
+    PKTHREAD Thread = KeGetCurrentThread();
+    LARGE_INTEGER Timeout;
+    Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
+
+    InitThreadData(&ThreadData, Mutex, KeAcquireGuardedMutex, NULL, KeReleaseGuardedMutex);
+    InitThreadData(&ThreadData2, Mutex, KeAcquireGuardedMutex, NULL, KeReleaseGuardedMutex);
+    InitThreadData(&ThreadDataUnsafe, Mutex, KeAcquireGuardedMutexUnsafe, NULL, KeReleaseGuardedMutexUnsafe);
+    InitThreadData(&ThreadDataTry, Mutex, NULL, KeTryToAcquireGuardedMutex, KeReleaseGuardedMutex);
+
+    /* 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, 0x5555, -1, 0, 0, FALSE, 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, 0x5555, -1, 0, 0, FALSE, 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, 4L, ThreadData.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, 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, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* block two more threads */
+    Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
+    ok_eq_hex(Status, STATUS_TIMEOUT);
+    CheckMutex(Mutex, 4L, ThreadData2.Thread, 2LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+    Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
+    ok_eq_hex(Status, STATUS_TIMEOUT);
+    CheckMutex(Mutex, 8L, ThreadData2.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* finish 1 */
+    FinishThread(&ThreadData2);
+    Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckMutex(Mutex, 4L, ThreadDataUnsafe.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, 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, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* finish 3 */
+    FinishThread(&ThreadData);
+
+    CheckMutex(Mutex, 1L, NULL, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
+}
+
 START_TEST(KeGuardedMutex)
 {
     KGUARDED_MUTEX Mutex;
@@ -174,4 +353,9 @@ START_TEST(KeGuardedMutex)
         Thread->KernelApcDisable = 0;
         KeLowerIrql(OldIrql);
     }
+
+    trace("Concurrent test\n");
+    RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
+    KeInitializeGuardedMutex(&Mutex);
+    TestGuardedMutexConcurrent(&Mutex);
 }