[KMTESTS:KE]
[reactos.git] / rostests / kmtests / ntos_ke / KeMutex.c
index 0670e8a..3177ae8 100644 (file)
@@ -7,6 +7,13 @@
 
 #include <kmt_test.h>
 
+static
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_max_(DISPATCH_LEVEL)
+BOOLEAN
+(NTAPI
+*pKeAreAllApcsDisabled)(VOID);
+
 #define ULONGS_PER_POINTER (sizeof(PVOID) / sizeof(ULONG))
 #define MUTANT_SIZE (2 + 6 * ULONGS_PER_POINTER)
 
@@ -14,7 +21,7 @@ C_ASSERT(sizeof(DISPATCHER_HEADER) == 8 + 2 * sizeof(PVOID));
 C_ASSERT(sizeof(KMUTANT) == sizeof(DISPATCHER_HEADER) + 3 * sizeof(PVOID) + sizeof(PVOID));
 C_ASSERT(sizeof(KMUTANT) == MUTANT_SIZE * sizeof(ULONG));
 
-#define CheckMutex(Mutex, Held, New, ExpectedApcDisable) do {                   \
+#define CheckMutex(Mutex, State, New, ExpectedApcDisable) do {                  \
     PKTHREAD Thread = KeGetCurrentThread();                                     \
     ok_eq_uint((Mutex)->Header.Type, MutantObject);                             \
     ok_eq_uint((Mutex)->Header.Abandoned, 0x55);                                \
@@ -24,9 +31,9 @@ C_ASSERT(sizeof(KMUTANT) == MUTANT_SIZE * sizeof(ULONG));
                   &(Mutex)->Header.WaitListHead);                               \
     ok_eq_pointer((Mutex)->Header.WaitListHead.Blink,                           \
                   &(Mutex)->Header.WaitListHead);                               \
-    if (Held)                                                                   \
+    if ((State) <= 0)                                                           \
     {                                                                           \
-        ok_eq_long((Mutex)->Header.SignalState, 0);                             \
+        ok_eq_long((Mutex)->Header.SignalState, State);                         \
         ok_eq_pointer((Mutex)->MutantListEntry.Flink, &Thread->MutantListHead); \
         ok_eq_pointer((Mutex)->MutantListEntry.Blink, &Thread->MutantListHead); \
         ok_eq_pointer(Thread->MutantListHead.Flink, &(Mutex)->MutantListEntry); \
@@ -35,7 +42,7 @@ C_ASSERT(sizeof(KMUTANT) == MUTANT_SIZE * sizeof(ULONG));
     }                                                                           \
     else                                                                        \
     {                                                                           \
-        ok_eq_long((Mutex)->Header.SignalState, 1);                             \
+        ok_eq_long((Mutex)->Header.SignalState, State);                         \
         if (New)                                                                \
         {                                                                       \
             ok_eq_pointer((Mutex)->MutantListEntry.Flink,                       \
@@ -55,7 +62,8 @@ C_ASSERT(sizeof(KMUTANT) == MUTANT_SIZE * sizeof(ULONG));
 {                                                                                       \
     ok_eq_bool(KeAreApcsDisabled(), KernelApcsDisabled || SpecialApcsDisabled);         \
     ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled);                            \
-    ok_eq_bool(KeAreAllApcsDisabled(), AllApcsDisabled);                                \
+    if (pKeAreAllApcsDisabled)                                                          \
+        ok_eq_bool(pKeAreAllApcsDisabled(), AllApcsDisabled);                           \
     ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled);                          \
     ok_irql(Irql);                                                                      \
 } while (0)
@@ -67,21 +75,22 @@ TestMutant(VOID)
     NTSTATUS Status;
     KMUTANT Mutant;
     LONG State;
+    LONG i;
     PKTHREAD Thread = KeGetCurrentThread();
 
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
     RtlFillMemory(&Mutant, sizeof(Mutant), 0x55);
     KeInitializeMutant(&Mutant, FALSE);
-    CheckMutex(&Mutant, FALSE, TRUE, 0);
+    CheckMutex(&Mutant, 1L, TRUE, 0);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
 
     RtlFillMemory(&Mutant, sizeof(Mutant), 0x55);
     KeInitializeMutant(&Mutant, TRUE);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
-    CheckMutex(&Mutant, TRUE, TRUE, 0);
+    CheckMutex(&Mutant, 0L, TRUE, 0);
     State = KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
-    ok_eq_long(State, 0);
-    CheckMutex(&Mutant, FALSE, FALSE, 0);
+    ok_eq_long(State, 0L);
+    CheckMutex(&Mutant, 1L, FALSE, 0);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
 
     /* Acquire and release */
@@ -91,12 +100,99 @@ TestMutant(VOID)
                                    FALSE,
                                    NULL);
     ok_eq_hex(Status, STATUS_SUCCESS);
-    CheckMutex(&Mutant, TRUE, TRUE, 0);
+    CheckMutex(&Mutant, 0L, TRUE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    State = KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
+    ok_eq_long(State, 0L);
+    CheckMutex(&Mutant, 1L, FALSE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* Acquire recursively */
+    for (i = 0; i < 8; i++)
+    {
+        KmtStartSeh()
+            Status = KeWaitForSingleObject(&Mutant,
+                                           Executive,
+                                           KernelMode,
+                                           FALSE,
+                                           NULL);
+        KmtEndSeh(STATUS_SUCCESS);
+        ok_eq_hex(Status, STATUS_SUCCESS);
+        CheckMutex(&Mutant, -i, FALSE, 0);
+        CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+    }
+
+    for (i = 0; i < 7; i++)
+    {
+        KmtStartSeh()
+            State = KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
+        KmtEndSeh(STATUS_SUCCESS);
+        ok_eq_long(State, -7L + i);
+        CheckMutex(&Mutant, -6L + i, FALSE, 0);
+        CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+    }
+
+    State = KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
+    ok_eq_long(State, 0L);
+    CheckMutex(&Mutant, 1L, FALSE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* Pretend to acquire it recursively -MINLONG times */
+    KmtStartSeh()
+        Status = KeWaitForSingleObject(&Mutant,
+                                       Executive,
+                                       KernelMode,
+                                       FALSE,
+                                       NULL);
+    KmtEndSeh(STATUS_SUCCESS);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckMutex(&Mutant, 0L, FALSE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    Mutant.Header.SignalState = MINLONG + 1;
+    KmtStartSeh()
+        Status = KeWaitForSingleObject(&Mutant,
+                                       Executive,
+                                       KernelMode,
+                                       FALSE,
+                                       NULL);
+    KmtEndSeh(STATUS_SUCCESS);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckMutex(&Mutant, (LONG)MINLONG, FALSE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    KmtStartSeh()
+        KeWaitForSingleObject(&Mutant,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+    KmtEndSeh(STATUS_MUTANT_LIMIT_EXCEEDED);
+    CheckMutex(&Mutant, (LONG)MINLONG, FALSE, 0);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
 
     State = KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
-    ok_eq_long(State, 0);
-    CheckMutex(&Mutant, FALSE, FALSE, 0);
+    ok_eq_long(State, (LONG)MINLONG);
+    CheckMutex(&Mutant, (LONG)MINLONG + 1L, FALSE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    Mutant.Header.SignalState = -1;
+    State = KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
+    ok_eq_long(State, -1L);
+    CheckMutex(&Mutant, 0L, FALSE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    State = KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
+    ok_eq_long(State, 0L);
+    CheckMutex(&Mutant, 1L, FALSE, 0);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* Now release it once too often */
+    KmtStartSeh()
+        KeReleaseMutant(&Mutant, 1, FALSE, FALSE);
+    KmtEndSeh(STATUS_MUTANT_NOT_OWNED);
+    CheckMutex(&Mutant, 1L, FALSE, 0);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
 }
 
@@ -107,36 +203,131 @@ TestMutex(VOID)
     NTSTATUS Status;
     KMUTEX Mutex;
     LONG State;
+    LONG i;
     PKTHREAD Thread = KeGetCurrentThread();
 
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
     RtlFillMemory(&Mutex, sizeof(Mutex), 0x55);
     KeInitializeMutex(&Mutex, 0);
-    CheckMutex(&Mutex, FALSE, TRUE, 1);
+    CheckMutex(&Mutex, 1L, TRUE, 1);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
 
     RtlFillMemory(&Mutex, sizeof(Mutex), 0x55);
     KeInitializeMutex(&Mutex, 123);
-    CheckMutex(&Mutex, FALSE, TRUE, 1);
+    CheckMutex(&Mutex, 1L, TRUE, 1);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
 
+    /* Acquire and release */
     Status = KeWaitForSingleObject(&Mutex,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    NULL);
     ok_eq_hex(Status, STATUS_SUCCESS);
-    CheckMutex(&Mutex, TRUE, FALSE, 1);
+    CheckMutex(&Mutex, 0L, FALSE, 1);
+    CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+
+    State = KeReleaseMutex(&Mutex, FALSE);
+    ok_eq_long(State, 0L);
+    CheckMutex(&Mutex, 1L, FALSE, 1);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* Acquire recursively */
+    for (i = 0; i < 8; i++)
+    {
+        KmtStartSeh()
+            Status = KeWaitForSingleObject(&Mutex,
+                                           Executive,
+                                           KernelMode,
+                                           FALSE,
+                                           NULL);
+        KmtEndSeh(STATUS_SUCCESS);
+        ok_eq_hex(Status, STATUS_SUCCESS);
+        CheckMutex(&Mutex, -i, FALSE, 1);
+        CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+    }
+
+    for (i = 0; i < 7; i++)
+    {
+        KmtStartSeh()
+            State = KeReleaseMutex(&Mutex, FALSE);
+        KmtEndSeh(STATUS_SUCCESS);
+        ok_eq_long(State, -7L + i);
+        CheckMutex(&Mutex, -6L + i, FALSE, 1);
+        CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+    }
+
+    State = KeReleaseMutex(&Mutex, FALSE);
+    ok_eq_long(State, 0L);
+    CheckMutex(&Mutex, 1L, FALSE, 1);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* Pretend to acquire it recursively -MINLONG times */
+    KmtStartSeh()
+        Status = KeWaitForSingleObject(&Mutex,
+                                       Executive,
+                                       KernelMode,
+                                       FALSE,
+                                       NULL);
+    KmtEndSeh(STATUS_SUCCESS);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckMutex(&Mutex, 0L, FALSE, 1);
+    CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+
+    Mutex.Header.SignalState = MINLONG + 1;
+    KmtStartSeh()
+        Status = KeWaitForSingleObject(&Mutex,
+                                       Executive,
+                                       KernelMode,
+                                       FALSE,
+                                       NULL);
+    KmtEndSeh(STATUS_SUCCESS);
+    ok_eq_hex(Status, STATUS_SUCCESS);
+    CheckMutex(&Mutex, (LONG)MINLONG, FALSE, 1);
+    CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+
+    KmtStartSeh()
+        KeWaitForSingleObject(&Mutex,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+    KmtEndSeh(STATUS_MUTANT_LIMIT_EXCEEDED);
+    CheckMutex(&Mutex, (LONG)MINLONG, FALSE, 1);
+    CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+
+    State = KeReleaseMutex(&Mutex, FALSE);
+    ok_eq_long(State, (LONG)MINLONG);
+    CheckMutex(&Mutex, (LONG)MINLONG + 1L, FALSE, 1);
     CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
 
+    Mutex.Header.SignalState = -1;
     State = KeReleaseMutex(&Mutex, FALSE);
-    ok_eq_long(State, 0);
-    CheckMutex(&Mutex, FALSE, FALSE, 1);
+    ok_eq_long(State, -1L);
+    CheckMutex(&Mutex, 0L, FALSE, 1);
+    CheckApcs(-1, 0, FALSE, PASSIVE_LEVEL);
+
+    State = KeReleaseMutex(&Mutex, FALSE);
+    ok_eq_long(State, 0L);
+    CheckMutex(&Mutex, 1L, FALSE, 1);
+    CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
+
+    /* Now release it once too often */
+    KmtStartSeh()
+        KeReleaseMutex(&Mutex, FALSE);
+    KmtEndSeh(STATUS_MUTANT_NOT_OWNED);
+    CheckMutex(&Mutex, 1L, FALSE, 1);
     CheckApcs(0, 0, FALSE, PASSIVE_LEVEL);
 }
 
 START_TEST(KeMutex)
 {
+    pKeAreAllApcsDisabled = KmtGetSystemRoutineAddress(L"KeAreAllApcsDisabled");
+    if (skip(pKeAreAllApcsDisabled != NULL, "KeAreAllApcsDisabled unavailable\n"))
+    {
+        /* We can live without this function here */
+    }
+
     TestMutant();
     TestMutex();
 }