server_select can now trywait on native semaphores in a select_op when wait conditions do not require the thread to block. Otherwise, a standard server call is made.
Signed-off-by: Daniel Santos daniel.santos@pobox.com --- dlls/ntdll/server.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+)
diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c index 763a90d..b186791 100644 --- a/dlls/ntdll/server.c +++ b/dlls/ntdll/server.c @@ -83,6 +83,7 @@ #include "ntdll_misc.h"
WINE_DEFAULT_DEBUG_CHANNEL(server); +WINE_DECLARE_DEBUG_CHANNEL(ntdllsync);
/* Some versions of glibc don't define this */ #ifndef SCM_RIGHTS @@ -594,6 +595,138 @@ unsigned int server_select( const select_op_t *select_op, data_size_t size, UINT
memset( &result, 0, sizeof(result) );
+#if ENABLE_POSIX_SYNC + TRACE_(ntdllsync)("select_op = %p, size = %u, flags = 0x%x, timeout = %p (%zd)\n", + select_op, size, flags, timeout, (timeout ? timeout->QuadPart : 0)); + + if (select_op && (select_op->op == SELECT_WAIT || select_op->op == SELECT_WAIT_ALL)) + { + struct ntdll_object *objs[MAXIMUM_WAIT_OBJECTS]; + size_t nb_locally_lockable_objs = 0; + size_t nb_handles = (size - offsetof( select_op_t, wait.handles )) + / sizeof(select_op->wait.handles[0]); + ssize_t i; + NTSTATUS result; + NTSTATUS ret = STATUS_UNSUCCESSFUL; + ssize_t wait_object = 0; + + /* count the number of locally lockable objects & cache their pointers */ + for (i = 0; i < nb_handles; ++i) + { + objs[i] = ntdll_handle_find(wine_server_ptr_handle(select_op->wait.handles[i])); + if (objs[i] && objs[i]->ops->trywait) + ++nb_locally_lockable_objs; + } + + TRACE_(ntdllsync)("%zd/%zd objects are locally selectable, need %s.\n", + nb_locally_lockable_objs, nb_handles, + (select_op->op == SELECT_WAIT ? "any" : "all")); + + /* bWaitAll = FALSE */ + if (select_op->op == SELECT_WAIT) + { + if (!nb_locally_lockable_objs) + goto local_done; + + /* waitformultipleobjectsex with bWaitAll = FALSE must go in order */ + for (i = 0; i < nb_handles; ++i) + { + /* If we can't lock this one locally, we must do the server call */ + if (!objs[i] || !objs[i]->ops->trywait) + break; + + result = objs[i]->ops->trywait(objs[i]); + if (result == STATUS_SUCCESS) + { + TRACE_(ntdllsync)("Successful local wait any. obj = %p (h = %p)\n", objs[i], objs[i]->h); + + ret = STATUS_SUCCESS; + wait_object = i; + break; + } + else if (result == STATUS_WAS_LOCKED) + continue; + else + ret = result; + } + } + + else /* select_op->op == SELECT_WAIT_ALL (bWaitAll = TRUE)*/ + { + ULONGLONG locked_objs = 0; + assert(MAXIMUM_WAIT_OBJECTS <= sizeof(locked_objs) * 8); + + if (nb_locally_lockable_objs != nb_handles) + goto local_done; + + result = 0; + + /* try to lock all objects */ + for (i = 0; i < nb_handles; ++i) + { + result = objs[i]->ops->trywait(objs[i]); + + if (result == STATUS_WAS_LOCKED) /* normal trywait failure */ + break; + else if (result != STATUS_POSSIBLE_DEADLOCK) + { + WARN("Possible deadlock in thread 0x%x, waiting on handle %p\n", GetCurrentThreadId(), objs[i]->h); + break; + } + else if (result != STATUS_SUCCESS) + { + ERR("object (handle = %p) trywait returned 0x%x\n", objs[i]->h, result); + break; + } + + /* keep track of the objects we've locked, max 64 */ + locked_objs |= 1LL << i; + TRACE_(ntdllsync)("locked object %zd/%zd\n", i, nb_handles); + } + + /* This is a little redundant, if result == STATUS_SUCCESS, then i should also be == nb_handles */ + if (result == STATUS_SUCCESS && i == nb_handles) + { + TRACE_(ntdllsync)("Successful local wait all. count = %zu\n", nb_handles); + ret = STATUS_SUCCESS; + } + /* if not successful then roll back locks */ + else + { + TRACE_(ntdllsync)("rolling back...\n"); + for (; i >= 0; --i) + { + TRACE_(ntdllsync)("rolling back %zd/%zd\n", i, nb_handles); + if (locked_objs & (1 << i)) + objs[i]->ops->trywait_undo(objs[i]); + } + + if (result != STATUS_WAS_LOCKED) + return result; + } + } + +local_done: + + /* release any objects we've grabbed */ + for (i = 0; i < nb_handles; ++i) + if (objs[i]) + ntdll_object_release(objs[i]); + + switch (ret) + { + case STATUS_SUCCESS: + return WAIT_OBJECT_0 + wait_object; + + default: + /* fall through to server call */ + break; + } + } +TRACE_(ntdllsync)("Doing server call.\n"); + +#endif /* ENABLE_POSIX_SYNC */ + for (;;) { SERVER_START_REQ( select )