#include <kmt_test.h>
-/* TODO: why does GCC have 3 tests less than MSVC?! */
-
#define CheckEvent(Event, ExpectedType, State, ExpectedWaitNext, \
Irql, ThreadList, ThreadCount) do \
{ \
for (TheIndex = 0; TheIndex < (ThreadCount); ++TheIndex) \
{ \
TheThread = CONTAINING_RECORD(TheEntry, KTHREAD, \
- WaitBlock[0].WaitListEntry); \
+ WaitBlock[0].WaitListEntry); \
ok_eq_pointer(TheThread, (ThreadList)[TheIndex]); \
ok_eq_pointer(TheEntry->Flink->Blink, TheEntry); \
TheEntry = TheEntry->Flink; \
KPRIORITY Priority;
LARGE_INTEGER LongTimeout, ShortTimeout;
INT i;
- KWAIT_BLOCK WaitBlock[MAXIMUM_WAIT_OBJECTS];
- PVOID ThreadObjects[MAXIMUM_WAIT_OBJECTS];
+ KWAIT_BLOCK WaitBlock[RTL_NUMBER_OF(Threads)];
+ PVOID ThreadObjects[RTL_NUMBER_OF(Threads)];
LONG State;
PKTHREAD Thread = KeGetCurrentThread();
+ OBJECT_ATTRIBUTES ObjectAttributes;
LongTimeout.QuadPart = -100 * MILLISECOND;
ShortTimeout.QuadPart = -1 * MILLISECOND;
{
Threads[i].Event = Event;
Threads[i].Signal = FALSE;
- Status = PsCreateSystemThread(&Threads[i].Handle, GENERIC_ALL, NULL, NULL, NULL, WaitForEventThread, &Threads[i]);
+ InitializeObjectAttributes(&ObjectAttributes,
+ NULL,
+ OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+ Status = PsCreateSystemThread(&Threads[i].Handle, GENERIC_ALL, &ObjectAttributes, 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);
while (!Threads[i].Signal)
{
Status = KeDelayExecutionThread(KernelMode, FALSE, &ShortTimeout);
- ok_eq_hex(Status, STATUS_SUCCESS);
+ if (Status != STATUS_SUCCESS)
+ {
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ }
}
CheckEvent(Event, Type, 0L, FALSE, OriginalIrql, ThreadObjects, i + 1);
}
}
}
+#define NUM_SCHED_TESTS 1000
+
+typedef struct
+{
+ KEVENT Event;
+ KEVENT WaitEvent;
+ ULONG Counter;
+ KPRIORITY PriorityIncrement;
+ ULONG CounterValues[NUM_SCHED_TESTS];
+} COUNT_THREAD_DATA, *PCOUNT_THREAD_DATA;
+
+static
+VOID
+NTAPI
+CountThread(
+ IN OUT PVOID Context)
+{
+ PCOUNT_THREAD_DATA ThreadData = Context;
+ PKEVENT Event = &ThreadData->Event;
+ volatile ULONG *Counter = &ThreadData->Counter;
+ ULONG *CounterValue = ThreadData->CounterValues;
+ KPRIORITY Priority;
+
+ Priority = KeQueryPriorityThread(KeGetCurrentThread());
+ ok_eq_long(Priority, 8L);
+
+ while (CounterValue < &ThreadData->CounterValues[NUM_SCHED_TESTS])
+ {
+ KeSetEvent(&ThreadData->WaitEvent, IO_NO_INCREMENT, TRUE);
+ KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
+ *CounterValue++ = *Counter;
+ }
+
+ Priority = KeQueryPriorityThread(KeGetCurrentThread());
+ ok_eq_long(Priority, 8L + min(ThreadData->PriorityIncrement, 7));
+}
+
+static
+VOID
+NTAPI
+TestEventScheduling(
+ _In_ PVOID Context)
+{
+ PCOUNT_THREAD_DATA ThreadData;
+ PKTHREAD Thread;
+ NTSTATUS Status;
+ LONG PreviousState;
+ ULONG i;
+ volatile ULONG *Counter;
+ KPRIORITY PriorityIncrement;
+ KPRIORITY Priority;
+
+ UNREFERENCED_PARAMETER(Context);
+
+ ThreadData = ExAllocatePoolWithTag(PagedPool, sizeof(*ThreadData), 'CEmK');
+ if (skip(ThreadData != NULL, "Out of memory\n"))
+ {
+ return;
+ }
+ KeInitializeEvent(&ThreadData->Event, SynchronizationEvent, FALSE);
+ KeInitializeEvent(&ThreadData->WaitEvent, SynchronizationEvent, FALSE);
+ Counter = &ThreadData->Counter;
+
+ for (PriorityIncrement = 0; PriorityIncrement <= 8; PriorityIncrement++)
+ {
+ ThreadData->PriorityIncrement = PriorityIncrement;
+ ThreadData->Counter = 0;
+ RtlFillMemory(ThreadData->CounterValues,
+ sizeof(ThreadData->CounterValues),
+ 0xFE);
+ Thread = KmtStartThread(CountThread, ThreadData);
+ Priority = KeQueryPriorityThread(KeGetCurrentThread());
+ ok(Priority == 8, "[%lu] Priority = %lu\n", PriorityIncrement, Priority);
+ for (i = 1; i <= NUM_SCHED_TESTS; i++)
+ {
+ Status = KeWaitForSingleObject(&ThreadData->WaitEvent, Executive, KernelMode, FALSE, NULL);
+ ok_eq_hex(Status, STATUS_SUCCESS);
+ PreviousState = KeSetEvent(&ThreadData->Event, PriorityIncrement, FALSE);
+ *Counter = i;
+ ok_eq_long(PreviousState, 0L);
+ }
+ Priority = KeQueryPriorityThread(KeGetCurrentThread());
+ ok(Priority == 8, "[%lu] Priority = %lu\n", PriorityIncrement, Priority);
+ KmtFinishThread(Thread, NULL);
+
+ if (PriorityIncrement == 0)
+ {
+ /* Both threads have the same priority, so either can win the race */
+ ok(ThreadData->CounterValues[0] == 0 || ThreadData->CounterValues[0] == 1,
+ "[%lu] Counter 0 = %lu\n",
+ PriorityIncrement, ThreadData->CounterValues[0]);
+ }
+ else
+ {
+ /* CountThread has the higher priority, it will always win */
+ ok(ThreadData->CounterValues[0] == 0,
+ "[%lu] Counter 0 = %lu\n",
+ PriorityIncrement, ThreadData->CounterValues[0]);
+ }
+ for (i = 1; i < NUM_SCHED_TESTS; i++)
+ {
+ if (PriorityIncrement == 0)
+ {
+ ok(ThreadData->CounterValues[i] == i ||
+ ThreadData->CounterValues[i] == i + 1,
+ "[%lu] Counter %lu = %lu, expected %lu or %lu\n",
+ PriorityIncrement, i,
+ ThreadData->CounterValues[i], i, i + 1);
+ }
+ else
+ {
+ ok(ThreadData->CounterValues[i] == ThreadData->CounterValues[i - 1] + 1,
+ "[%lu] Counter %lu = %lu, expected %lu\n",
+ PriorityIncrement, i,
+ ThreadData->CounterValues[i], ThreadData->CounterValues[i - 1] + 1);
+ }
+ }
+ }
+
+ ExFreePoolWithTag(ThreadData, 'CEmK');
+}
+
START_TEST(KeEvent)
{
+ PKTHREAD Thread;
KEVENT Event;
KIRQL Irql;
KIRQL Irqls[] = { PASSIVE_LEVEL, APC_LEVEL, DISPATCH_LEVEL };
- INT i;
+ ULONG i;
KPRIORITY PriorityIncrement;
- for (i = 0; i < sizeof Irqls / sizeof Irqls[0]; ++i)
+ for (i = 0; i < RTL_NUMBER_OF(Irqls); ++i)
{
KeRaiseIrql(Irqls[i], &Irql);
TestEventFunctional(&Event, NotificationEvent, Irqls[i]);
KeLowerIrql(Irql);
}
- for (i = 0; i < sizeof Irqls / sizeof Irqls[0]; ++i)
+ for (i = 0; i < RTL_NUMBER_OF(Irqls); ++i)
{
/* creating threads above DISPATCH_LEVEL... nope */
if (Irqls[i] >= DISPATCH_LEVEL)
trace("IRQL: %u\n", Irqls[i]);
for (PriorityIncrement = -1; PriorityIncrement <= 8; ++PriorityIncrement)
{
+ if (PriorityIncrement < 0 && KmtIsCheckedBuild)
+ continue;
trace("PriorityIncrement: %ld\n", PriorityIncrement);
trace("-> Checking KeSetEvent, NotificationEvent\n");
TestEventConcurrent(&Event, NotificationEvent, Irqls[i], KeSetEvent, PriorityIncrement, 1, TRUE);
ok_irql(PASSIVE_LEVEL);
KmtSetIrql(PASSIVE_LEVEL);
+
+ Thread = KmtStartThread(TestEventScheduling, NULL);
+ KmtFinishThread(Thread, NULL);
}