From: William Horvath william@horvath.blog
The key change is to never return STATUS_TIMEOUT, and to instead return the result of NtYieldExecution() if zero timeout was passed, or STATUS_SUCCESS otherwise.
An overview of the correct values for each combination, copied from the test commit: - 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 --- dlls/ntdll/tests/sync.c | 6 ++---- dlls/ntdll/unix/server.c | 10 +++++++--- dlls/ntdll/unix/sync.c | 22 +++++++++++++++------- dlls/ntdll/unix/unix_private.h | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c index 77ec2792c13..cdfec5c51d6 100644 --- a/dlls/ntdll/tests/sync.c +++ b/dlls/ntdll/tests/sync.c @@ -1100,7 +1100,6 @@ static void test_delayexecution(void) /* 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 ); @@ -1111,7 +1110,6 @@ static void test_delayexecution(void) /* 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 ); @@ -1160,8 +1158,8 @@ static void test_delayexecution(void) } }
- 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( noyields[0] > 0, "no STATUS_NO_YIELD_PERFORMED results for non-alertable yields.\n" ); + 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" ); } diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index b40e7c2a1d7..0cc236ce21f 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -764,10 +764,10 @@ unsigned int server_select( const union select_op *select_op, data_size_t size, * server_wait */ unsigned int server_wait( const union select_op *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout ) + const LARGE_INTEGER *timeout, unsigned int *yield_result ) { timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE; - unsigned int ret; + unsigned int ret, status; struct user_apc apc;
if (abs_timeout < 0) @@ -784,7 +784,11 @@ unsigned int server_wait( const union select_op *select_op, data_size_t size, UI /* A test on Windows 2000 shows that Windows always yields during a wait, but a wait that is hit by an event gets a priority boost as well. This seems to model that behavior the closest. */ - if (ret == STATUS_TIMEOUT) NtYieldExecution(); + if (ret == STATUS_TIMEOUT) + { + status = NtYieldExecution(); + if (yield_result) *yield_result = status; + } return ret; }
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 0ee16b360b3..5401b76d12a 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -1580,7 +1580,7 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO if (alertable) flags |= SELECT_ALERTABLE; select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); - return server_wait( &select_op, offsetof( union select_op, wait.handles[count] ), flags, timeout ); + return server_wait( &select_op, offsetof( union select_op, wait.handles[count] ), flags, timeout, NULL ); }
@@ -1608,7 +1608,7 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; select_op.signal_and_wait.wait = wine_server_obj_handle( wait ); select_op.signal_and_wait.signal = wine_server_obj_handle( signal ); - return server_wait( &select_op, sizeof(select_op.signal_and_wait), flags, timeout ); + return server_wait( &select_op, sizeof(select_op.signal_and_wait), flags, timeout, NULL ); }
@@ -1641,8 +1641,16 @@ NTSTATUS WINAPI NtYieldExecution(void) */ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) { + unsigned int yield_result = STATUS_SUCCESS; + /* if alertable, we need to query the server */ - if (alertable) return server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout ); + if (alertable) + { + server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout, &yield_result ); + /* we only care about the return value of the yield for zero timeouts */ + if (timeout && !timeout->QuadPart) return yield_result; + return STATUS_SUCCESS; + }
if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ { @@ -1660,8 +1668,8 @@ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeou }
/* Note that we yield after establishing the desired timeout */ - NtYieldExecution(); - if (!when) return STATUS_SUCCESS; + yield_result = NtYieldExecution(); + if (!when) return yield_result;
for (;;) { @@ -1892,7 +1900,7 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key, select_op.keyed_event.op = SELECT_KEYED_EVENT_WAIT; select_op.keyed_event.handle = wine_server_obj_handle( handle ); select_op.keyed_event.key = wine_server_client_ptr( key ); - return server_wait( &select_op, sizeof(select_op.keyed_event), flags, timeout ); + return server_wait( &select_op, sizeof(select_op.keyed_event), flags, timeout, NULL ); }
@@ -1911,7 +1919,7 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key, select_op.keyed_event.op = SELECT_KEYED_EVENT_RELEASE; select_op.keyed_event.handle = wine_server_obj_handle( handle ); select_op.keyed_event.key = wine_server_client_ptr( key ); - return server_wait( &select_op, sizeof(select_op.keyed_event), flags, timeout ); + return server_wait( &select_op, sizeof(select_op.keyed_event), flags, timeout, NULL ); }
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index f840045f841..b93b37b003d 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -211,7 +211,7 @@ extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t extern unsigned int server_select( const union select_op *select_op, data_size_t size, UINT flags, timeout_t abs_timeout, struct context_data *context, struct user_apc *user_apc ); extern unsigned int server_wait( const union select_op *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout ); + const LARGE_INTEGER *timeout, unsigned int *yield_result ); extern unsigned int server_queue_process_apc( HANDLE process, const union apc_call *call, union apc_result *result ); extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd,