[PATCH 9/9] ntdll: add ability to trywait on semaphores locally

Daniel Santos daniel.santos at pobox.com
Thu Sep 10 18:26:11 CDT 2015


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 at 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 )
-- 
2.4.6




More information about the wine-devel mailing list