From: William Horvath william@horvath.blog
--- dlls/ntdll/tests/sync.c | 185 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+)
diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c index 3ab89bbf361..15f692e67bd 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,188 @@ 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 + */ + +struct test_delay_param +{ + HANDLE ready, completion; + volatile LONG apc_count; + NTSTATUS status; + BOOLEAN non_zero_timeout; +}; + +static VOID CALLBACK apc_func( ULONG_PTR arg ) +{ + struct test_delay_param *p = (struct test_delay_param *)arg; + InterlockedIncrement( &p->apc_count ); +} + +#define NUM_APCS 64 +#define NUM_ITERATIONS 32 + +static DWORD WINAPI delay_thread( void *arg ) +{ + struct test_delay_param *p = arg; + LARGE_INTEGER timeout = {{0}}; + NTSTATUS status; + DWORD ret; + int i; + + status = pNtDelayExecution( TRUE, &timeout ); + p->status = status; + SetEvent( p->ready ); + + ret = WaitForSingleObject( p->completion, INFINITE ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed with %#lx\n", ret ); + + if (p->non_zero_timeout) + timeout.QuadPart = -10000; + + for (i = 0; i < NUM_ITERATIONS; i++) + { + status = pNtDelayExecution( TRUE, &timeout ); + if (!p->non_zero_timeout) + { + todo_wine_if(status == STATUS_TIMEOUT) + ok( status == STATUS_USER_APC || status == STATUS_NO_YIELD_PERFORMED || + status == STATUS_SUCCESS, "NtDelayExecution iteration %d returned %#lx\n", + i, status ); + } + else + { + todo_wine_if(status == STATUS_TIMEOUT) + ok( status == STATUS_USER_APC || status == STATUS_SUCCESS, + "NtDelayExecution iteration %d with timeout returned %#lx\n", + i, status ); + } + + if ((i % 10) == 0) + Sleep( 1 ); + } + + ok( p->apc_count > 0, "no APCs were delivered\n" ); + ok( p->apc_count <= NUM_APCS, "got %ld APCs, expected at most %d\n", + p->apc_count, NUM_APCS ); + + return 0; +} + +static void test_delayexecution(void) +{ + static const struct test + { + LONGLONG timeout; + BOOLEAN alertable; + } + tests[] = + { + { 0, FALSE }, + { 0, TRUE }, + { 1 * -10000, FALSE }, + { 1 * -10000, TRUE }, + { 10 * -10000, TRUE }, + }; + + struct test_delay_param param = {0}; + struct test_delay_param param_timeout = {0}; + unsigned int total_noyields[2] = {0, 0}; + LARGE_INTEGER timeout; + HANDLE thread, thread_timeout; + NTSTATUS status; + DWORD tid, tid_timeout, ret; + unsigned int i, j; + + for (i = 0; i < NUM_ITERATIONS; i++) + { + for (j = 0; j < ARRAY_SIZE(tests); j++) + { + timeout.QuadPart = tests[j].timeout; + status = pNtDelayExecution( tests[j].alertable, &timeout ); + + if (!tests[j].alertable) + { + ok( status == STATUS_SUCCESS || status == STATUS_NO_YIELD_PERFORMED, + "test %u: got %#lx, expected SUCCESS or NO_YIELD_PERFORMED\n", j, status ); + if (status == STATUS_NO_YIELD_PERFORMED) total_noyields[0]++; + } + else if (!timeout.QuadPart) + { + todo_wine_if(status == STATUS_TIMEOUT) + ok( status == STATUS_SUCCESS || status == STATUS_NO_YIELD_PERFORMED || status == STATUS_USER_APC, + "test %u: got %#lx, expected SUCCESS, NO_YIELD_PERFORMED, or USER_APC\n", j, status ); + if (status == STATUS_NO_YIELD_PERFORMED) total_noyields[1]++; + } + else + { + todo_wine_if(status == STATUS_TIMEOUT) + ok( status == STATUS_SUCCESS || status == STATUS_USER_APC, + "test %u: got %#lx, expected SUCCESS or USER_APC\n", j, status ); + } + } + } + + todo_wine ok( total_noyields[0] > 0, "Expected > 0 STATUS_NO_YIELD_PERFORMED results for non-alertable zero-timeout delays.\n" ); + todo_wine ok( total_noyields[1] > 0, "Expected > 0 STATUS_NO_YIELD_PERFORMED results for alertable zero-timeout delays.\n" ); + + param.ready = CreateEventA( NULL, FALSE, FALSE, NULL ); + ok( param.ready != NULL, "CreateEvent failed, error %lu\n", GetLastError() ); + param.completion = CreateEventA( NULL, FALSE, FALSE, NULL ); + ok( param.completion != NULL, "CreateEvent failed, error %lu\n", GetLastError() ); + param.non_zero_timeout = FALSE; + + param_timeout = param; + param_timeout.ready = CreateEventA( NULL, FALSE, FALSE, NULL ); + ok( param_timeout.ready != NULL, "CreateEvent failed, error %lu\n", GetLastError() ); + param_timeout.completion = CreateEventA( NULL, FALSE, FALSE, NULL ); + ok( param_timeout.completion != NULL, "CreateEvent failed, error %lu\n", GetLastError() ); + param_timeout.non_zero_timeout = TRUE; + + thread = CreateThread( NULL, 0, delay_thread, ¶m, 0, &tid ); + ok( thread != NULL, "CreateThread failed, error %lu\n", GetLastError() ); + thread_timeout = CreateThread( NULL, 0, delay_thread, ¶m_timeout, 0, &tid_timeout ); + ok( thread_timeout != NULL, "CreateThread failed, error %lu\n", GetLastError() ); + + ret = WaitForSingleObject( param.ready, 5000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed with %#lx\n", ret ); + ret = WaitForSingleObject( param_timeout.ready, 5000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed with %#lx\n", ret ); + + todo_wine + ok( param.status == STATUS_SUCCESS || param.status == STATUS_NO_YIELD_PERFORMED, + "thread NtDelayExecution returned %#lx\n", param.status ); + todo_wine + ok( param_timeout.status == STATUS_SUCCESS || param_timeout.status == STATUS_NO_YIELD_PERFORMED, + "thread NtDelayExecution (timeout) returned %#lx\n", param_timeout.status ); + + for (i = 0; i < NUM_APCS; i++) + { + ok( QueueUserAPC( apc_func, thread, (ULONG_PTR)¶m ), + "QueueUserAPC failed for zero timeout thread, error %lu\n", GetLastError() ); + ok( QueueUserAPC( apc_func, thread_timeout, (ULONG_PTR)¶m_timeout ), + "QueueUserAPC failed for non-zero timeout thread, error %lu\n", GetLastError() ); + } + + SetEvent( param.completion ); + SetEvent( param_timeout.completion ); + + ret = WaitForSingleObject( thread, 5000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed with %#lx\n", ret ); + ret = WaitForSingleObject( thread_timeout, 5000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed with %#lx\n", ret ); + + CloseHandle( thread ); + CloseHandle( thread_timeout ); + CloseHandle( param.ready ); + CloseHandle( param.completion ); + CloseHandle( param_timeout.ready ); + CloseHandle( param_timeout.completion ); +} + START_TEST(sync) { HMODULE module = GetModuleHandleA("ntdll.dll"); @@ -1046,6 +1229,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 +1262,5 @@ START_TEST(sync) test_resource(); test_tid_alert( argv ); test_completion_port_scheduling(); + test_delayexecution(); }