From: William Horvath william@horvath.blog
--- dlls/ntdll/tests/sync.c | 138 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+)
diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c index 3ab89bbf361..77ec2792c13 100644 --- a/dlls/ntdll/tests/sync.c +++ b/dlls/ntdll/tests/sync.c @@ -32,6 +32,7 @@ static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const OBJECT_AT static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG ); static NTSTATUS (WINAPI *pNtCreateMutant)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, BOOLEAN ); static NTSTATUS (WINAPI *pNtCreateSemaphore)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, LONG, LONG ); +static NTSTATUS (WINAPI *pNtDelayExecution)( BOOLEAN, const LARGE_INTEGER * ); static NTSTATUS (WINAPI *pNtOpenEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); static NTSTATUS (WINAPI *pNtOpenKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); static NTSTATUS (WINAPI *pNtPulseEvent)( HANDLE, LONG * ); @@ -1030,6 +1031,141 @@ static void test_completion_port_scheduling(void) } }
+/* An overview of possible combinations and return values: + * - Non-alertable, zero timeout: STATUS_SUCCESS or STATUS_NO_YIELD_PERFORMED + * - Non-alertable, non-zero timeout: STATUS_SUCCESS + * - Alertable, zero timeout: STATUS_SUCCESS, STATUS_NO_YIELD_PERFORMED, or STATUS_USER_APC + * - Alertable, non-zero timeout: STATUS_SUCCESS or STATUS_USER_APC + * - Sleep/SleepEx don't modify LastError, no matter what + */ + +static VOID CALLBACK apc_proc( ULONG_PTR param ) +{ + InterlockedIncrement( (LONG *)param ); +} + +struct delay_param +{ + BOOLEAN alertable; + LONGLONG timeout; + BOOLEAN queue_apc; + const char *desc; +}; + +static const struct delay_param tests[] = +{ + /* "yield" means zero-timeout delay */ + /* non-alertable tests */ + { FALSE, 0, FALSE, "non-alertable yield" }, + { FALSE, -10000, FALSE, "non-alertable sleep" }, + /* alertable tests without APCs */ + { TRUE, 0, FALSE, "alertable yield" }, + { TRUE, -10000, FALSE, "alertable sleep" }, + /* alertable tests with APCs */ + { TRUE, 0, TRUE, "alertable yield with APC" }, + { TRUE, -10000, TRUE, "alertable sleep with APC" }, +}; + +static void test_delayexecution(void) +{ + unsigned int i, j; + unsigned int noyields[2] = {0, 0}; /* [0] = non-alertable, [1] = alertable */ + unsigned int apc_status_count[2] = {0, 0}; /* [0] = zero timeout, [1] = non-zero timeout */ + LARGE_INTEGER timeout; + ULONG apc_count; + NTSTATUS status; + DWORD ret; + + for (i = 0; i < 50; i++) + { + for (j = 0; j < ARRAY_SIZE(tests); j++) + { + const struct delay_param *test = &tests[j]; + timeout.QuadPart = test->timeout; + apc_count = 0; + + if (test->queue_apc) + { + int k; + for (k = 0; k < 8; k++) + ok( QueueUserAPC( apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ), + "%s: failed to queue APC %d.\n", test->desc, k ); + } + + /* test NtDelayExecution */ + SetLastError( 0xdeadbeef ); + status = pNtDelayExecution( test->alertable, &timeout ); + ok( GetLastError() == 0xdeadbeef, "%s: LastError unexpectedly changed to %lu.\n", test->desc, GetLastError() ); + + /* test yields */ + if (!test->timeout) + { + todo_wine_if(status == STATUS_TIMEOUT) + ok( status == STATUS_SUCCESS || status == STATUS_NO_YIELD_PERFORMED || + (test->alertable && test->queue_apc && status == STATUS_USER_APC), + "%s: got %#lx.\n", test->desc, status ); + + if (status == STATUS_NO_YIELD_PERFORMED) + noyields[test->alertable ? 1 : 0]++; + } + /* test delays */ + else + { + todo_wine_if(status == STATUS_TIMEOUT) + ok( status == STATUS_SUCCESS || + (test->alertable && test->queue_apc && status == STATUS_USER_APC), + "%s: got %#lx.\n", test->desc, status ); + } + + /* make sure we only get APCs when we expect them */ + if (test->alertable && test->queue_apc) + { + if (status == STATUS_USER_APC) + apc_status_count[test->timeout ? 1 : 0]++; + ok( apc_count > 0, "%s: no APCs executed.\n", test->desc ); + } + else + ok( !apc_count, "%s: unexpected APCs executed %ld.\n", test->desc, apc_count ); + + /* test SleepEx */ + apc_count = 0; + if (test->queue_apc) + { + int k; + for (k = 0; k < 8; k++) + ok( QueueUserAPC( apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ), + "%s: failed to queue APC %d.\n", test->desc, k ); + } + + SetLastError( 0xdeadbeef ); + ret = SleepEx( timeout.QuadPart ? (-timeout.QuadPart / 10000) : 0, test->alertable ); + ok( GetLastError() == 0xdeadbeef, "%s: LastError unexpectedly changed to %lu.\n", test->desc, GetLastError() ); + + if (!test->alertable) + ok( !ret, "%s: SleepEx returned %lu.\n", test->desc, ret ); + else + ok( !ret || ret == WAIT_IO_COMPLETION, + "%s: SleepEx returned %lu.\n", test->desc, ret ); + + if (test->queue_apc) + ok( apc_count > 0, "%s: no APCs executed.\n", test->desc ); + + /* test Sleep (non-alertable only) */ + if (!test->alertable) + { + SetLastError( 0xdeadbeef ); + Sleep( timeout.QuadPart ? (-timeout.QuadPart / 10000) : 0 ); + ok( GetLastError() == 0xdeadbeef, "%s: LastError unexpectedly changed to %lu.\n", test->desc, GetLastError() ); + } + } + } + + todo_wine ok( noyields[0] > 0, "no STATUS_NO_YIELD_PERFORMED results for non-alertable yields.\n" ); + todo_wine ok( noyields[1] > 0, "no STATUS_NO_YIELD_PERFORMED results for alertable yields.\n" ); + ok( apc_status_count[0] > 0, "no STATUS_USER_APC results for alertable yields.\n" ); + ok( apc_status_count[1] > 0, "no STATUS_USER_APC results for alertable delays.\n" ); +} + START_TEST(sync) { HMODULE module = GetModuleHandleA("ntdll.dll"); @@ -1046,6 +1182,7 @@ START_TEST(sync) pNtCreateKeyedEvent = (void *)GetProcAddress(module, "NtCreateKeyedEvent"); pNtCreateMutant = (void *)GetProcAddress(module, "NtCreateMutant"); pNtCreateSemaphore = (void *)GetProcAddress(module, "NtCreateSemaphore"); + pNtDelayExecution = (void *)GetProcAddress(module, "NtDelayExecution"); pNtOpenEvent = (void *)GetProcAddress(module, "NtOpenEvent"); pNtOpenKeyedEvent = (void *)GetProcAddress(module, "NtOpenKeyedEvent"); pNtPulseEvent = (void *)GetProcAddress(module, "NtPulseEvent"); @@ -1078,4 +1215,5 @@ START_TEST(sync) test_resource(); test_tid_alert( argv ); test_completion_port_scheduling(); + test_delayexecution(); }