Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntoskrnl.exe/tests/driver.c | 311 ++++++++++++++++++++++++++++++- include/ddk/wdm.h | 2 + 2 files changed, 312 insertions(+), 1 deletion(-)
diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index 81baffa7cc..0dec481be9 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -322,7 +322,14 @@ todo_wine ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); }
-static void run_thread(PKSTART_ROUTINE proc, void *arg) +static void sleep(void) +{ + LARGE_INTEGER timeout; + timeout.QuadPart = -2000; + KeDelayExecutionThread( KernelMode, FALSE, &timeout ); +} + +static HANDLE create_thread(PKSTART_ROUTINE proc, void *arg) { OBJECT_ATTRIBUTES attr = {0}; HANDLE thread; @@ -333,12 +340,25 @@ static void run_thread(PKSTART_ROUTINE proc, void *arg) ret = PsCreateSystemThread(&thread, THREAD_ALL_ACCESS, &attr, NULL, NULL, proc, arg); ok(!ret, "got %#x\n", ret);
+ return thread; +} + +static void join_thread(HANDLE thread) +{ + NTSTATUS ret; + ret = ZwWaitForSingleObject(thread, FALSE, NULL); ok(!ret, "got %#x\n", ret); ret = ZwClose(thread); ok(!ret, "got %#x\n", ret); }
+static void run_thread(PKSTART_ROUTINE proc, void *arg) +{ + HANDLE thread = create_thread(proc, arg); + join_thread(thread); +} + static KMUTEX test_mutex;
static void WINAPI mutex_thread(void *arg) @@ -853,6 +873,294 @@ static void test_ob_reference(const WCHAR *test_path) ok(!status, "ZwClose failed: %#x\n", status); }
+static void check_resource_(int line, ERESOURCE *resource, ULONG exclusive_waiters, + ULONG shared_waiters, BOOLEAN exclusive, ULONG shared_count) +{ + BOOLEAN ret; + ULONG count; + + count = ExGetExclusiveWaiterCount(resource); + ok_(__FILE__, line, count == exclusive_waiters, + "expected %u exclusive waiters, got %u\n", exclusive_waiters, count); + count = ExGetSharedWaiterCount(resource); + ok_(__FILE__, line, count == shared_waiters, + "expected %u shared waiters, got %u\n", shared_waiters, count); + ret = ExIsResourceAcquiredExclusiveLite(resource); + ok_(__FILE__, line, ret == exclusive, + "expected exclusive %u, got %u\n", exclusive, ret); + count = ExIsResourceAcquiredSharedLite(resource); + ok_(__FILE__, line, count == shared_count, + "expected shared %u, got %u\n", shared_count, count); +} +#define check_resource(a,b,c,d,e) check_resource_(__LINE__,a,b,c,d,e) + +static KEVENT resource_shared_ready, resource_shared_done, resource_exclusive_ready, resource_exclusive_done; + +static void WINAPI resource_shared_thread(void *arg) +{ + ERESOURCE *resource = arg; + BOOLEAN ret; + + check_resource(resource, 0, 0, FALSE, 0); + + ret = ExAcquireResourceSharedLite(resource, TRUE); + ok(ret == TRUE, "got ret %u\n", ret); + + check_resource(resource, 0, 0, FALSE, 1); + + KeSetEvent(&resource_shared_ready, IO_NO_INCREMENT, FALSE); + KeWaitForSingleObject(&resource_shared_done, Executive, KernelMode, FALSE, NULL); + + ExReleaseResourceForThreadLite(resource, (ULONG_PTR)PsGetCurrentThread()); + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +static void WINAPI resource_exclusive_thread(void *arg) +{ + ERESOURCE *resource = arg; + BOOLEAN ret; + + check_resource(resource, 0, 0, FALSE, 0); + + ret = ExAcquireResourceExclusiveLite(resource, TRUE); + ok(ret == TRUE, "got ret %u\n", ret); + + check_resource(resource, 0, 0, TRUE, 1); + + KeSetEvent(&resource_exclusive_ready, IO_NO_INCREMENT, FALSE); + KeWaitForSingleObject(&resource_exclusive_done, Executive, KernelMode, FALSE, NULL); + + ExReleaseResourceForThreadLite(resource, (ULONG_PTR)PsGetCurrentThread()); + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +static void test_resource(void) +{ + ERESOURCE resource; + NTSTATUS status; + BOOLEAN ret; + HANDLE thread, thread2; + + memset(&resource, 0xcc, sizeof(resource)); + + status = ExInitializeResourceLite(&resource); + ok(status == STATUS_SUCCESS, "got status %#x\n", status); + check_resource(&resource, 0, 0, FALSE, 0); + + KeEnterCriticalRegion(); + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, TRUE, 1); + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, TRUE, 2); + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, TRUE, 3); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, TRUE, 2); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, TRUE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 1); + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 2); + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 2); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireSharedStarveExclusive(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireSharedWaitForExclusive(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 0); + + /* Do not acquire the resource ourselves, but spawn a shared thread holding it. */ + + KeInitializeEvent(&resource_shared_ready, SynchronizationEvent, FALSE); + KeInitializeEvent(&resource_shared_done, SynchronizationEvent, FALSE); + thread = create_thread(resource_shared_thread, &resource); + KeWaitForSingleObject(&resource_shared_ready, Executive, KernelMode, FALSE, NULL); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireSharedStarveExclusive(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireSharedWaitForExclusive(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + check_resource(&resource, 0, 0, FALSE, 0); + + KeSetEvent(&resource_shared_done, IO_NO_INCREMENT, FALSE); + join_thread(thread); + check_resource(&resource, 0, 0, FALSE, 0); + + /* Acquire the resource as exclusive, and then spawn a shared thread. */ + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, TRUE, 1); + + thread = create_thread(resource_shared_thread, &resource); + sleep(); + check_resource(&resource, 0, 1, TRUE, 1); + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 1, TRUE, 2); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + KeWaitForSingleObject(&resource_shared_ready, Executive, KernelMode, FALSE, NULL); + KeSetEvent(&resource_shared_done, IO_NO_INCREMENT, FALSE); + join_thread(thread); + check_resource(&resource, 0, 0, FALSE, 0); + + /* Do not acquire the resource ourselves, but spawn an exclusive thread holding it. */ + + KeInitializeEvent(&resource_exclusive_ready, SynchronizationEvent, FALSE); + KeInitializeEvent(&resource_exclusive_done, SynchronizationEvent, FALSE); + thread = create_thread(resource_exclusive_thread, &resource); + KeWaitForSingleObject(&resource_exclusive_ready, Executive, KernelMode, FALSE, NULL); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireSharedStarveExclusive(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 0); + + ret = ExAcquireSharedWaitForExclusive(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 0); + + KeSetEvent(&resource_exclusive_done, IO_NO_INCREMENT, FALSE); + join_thread(thread); + check_resource(&resource, 0, 0, FALSE, 0); + + /* Acquire the resource as shared, and then spawn an exclusive waiter. */ + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 0, 0, FALSE, 1); + + thread = create_thread(resource_exclusive_thread, &resource); + sleep(); + check_resource(&resource, 1, 0, FALSE, 1); + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 1, 0, FALSE, 2); + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + + ret = ExAcquireSharedStarveExclusive(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 1, 0, FALSE, 2); + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + + ret = ExAcquireSharedWaitForExclusive(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 1, 0, FALSE, 1); + + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + KeWaitForSingleObject(&resource_exclusive_ready, Executive, KernelMode, FALSE, NULL); + KeSetEvent(&resource_exclusive_done, IO_NO_INCREMENT, FALSE); + join_thread(thread); + check_resource(&resource, 0, 0, FALSE, 0); + + /* Spawn a shared and then exclusive waiter. */ + + KeInitializeEvent(&resource_shared_ready, SynchronizationEvent, FALSE); + KeInitializeEvent(&resource_shared_done, SynchronizationEvent, FALSE); + thread = create_thread(resource_shared_thread, &resource); + KeWaitForSingleObject(&resource_shared_ready, Executive, KernelMode, FALSE, NULL); + check_resource(&resource, 0, 0, FALSE, 0); + + thread2 = create_thread(resource_exclusive_thread, &resource); + sleep(); + check_resource(&resource, 1, 0, FALSE, 0); + + ret = ExAcquireResourceExclusiveLite(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 1, 0, FALSE, 0); + + ret = ExAcquireResourceSharedLite(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 1, 0, FALSE, 0); + + ret = ExAcquireSharedStarveExclusive(&resource, FALSE); + ok(ret == TRUE, "got ret %u\n", ret); + check_resource(&resource, 1, 0, FALSE, 1); + ExReleaseResourceForThreadLite(&resource, (ULONG_PTR)PsGetCurrentThread()); + + ret = ExAcquireSharedWaitForExclusive(&resource, FALSE); + ok(ret == FALSE, "got ret %u\n", ret); + check_resource(&resource, 1, 0, FALSE, 0); + + KeSetEvent(&resource_shared_done, IO_NO_INCREMENT, FALSE); + join_thread(thread); + KeWaitForSingleObject(&resource_exclusive_ready, Executive, KernelMode, FALSE, NULL); + KeSetEvent(&resource_exclusive_done, IO_NO_INCREMENT, FALSE); + join_thread(thread2); + check_resource(&resource, 0, 0, FALSE, 0); + + KeLeaveCriticalRegion(); + + status = ExDeleteResourceLite(&resource); + ok(status == STATUS_SUCCESS, "got status %#x\n", status); +} + static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) { ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; @@ -895,6 +1203,7 @@ static NTSTATUS main_test(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *st test_stack_callout(); test_lookaside_list(); test_ob_reference(test_input->path); + test_resource();
/* print process report */ if (winetest_debug) diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h index dae83afb2e..0faf5ecd47 100644 --- a/include/ddk/wdm.h +++ b/include/ddk/wdm.h @@ -1580,6 +1580,7 @@ void WINAPI KeAcquireSpinLockAtDpcLevel(KSPIN_LOCK*); BOOLEAN WINAPI KeCancelTimer(KTIMER*); void WINAPI KeClearEvent(PRKEVENT); NTSTATUS WINAPI KeDelayExecutionThread(KPROCESSOR_MODE,BOOLEAN,LARGE_INTEGER*); +void WINAPI KeEnterCriticalRegion(void); PKTHREAD WINAPI KeGetCurrentThread(void); void WINAPI KeInitializeEvent(PRKEVENT,EVENT_TYPE,BOOLEAN); void WINAPI KeInitializeMutex(PRKMUTEX,ULONG); @@ -1587,6 +1588,7 @@ void WINAPI KeInitializeSemaphore(PRKSEMAPHORE,LONG,LONG); void WINAPI KeInitializeSpinLock(KSPIN_LOCK*); void WINAPI KeInitializeTimerEx(PKTIMER,TIMER_TYPE); void WINAPI KeInitializeTimer(KTIMER*); +void WINAPI KeLeaveCriticalRegion(void); void WINAPI KeQuerySystemTime(LARGE_INTEGER*); void WINAPI KeQueryTickCount(LARGE_INTEGER*); ULONG WINAPI KeQueryTimeIncrement(void);