From 91c96ca57892577e20be532ae58b423fea1750b2 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 14:44:54 -0500
Subject: [PATCH] server, ntdll: Also wait on the queue fd when waiting for
 driver events.

Normally the server handles this, by polling on the fd during its main loop.
The problem there is that the server only polls when the thread is waiting
for messages. We want to replicate that behaviour, otherwise wineserver spins
forever trying to wake up a thread that just doesn't care.

With this patch, I'm finally able to interact with winecfg. Next step is to
fix the 'drives' tab.
---
 dlls/ntdll/esync.c  | 44 ++++++++++++++++++++++++++++++++------------
 server/esync.c      |  7 +++++--
 server/event.c      |  5 +++--
 server/object.h     |  2 +-
 server/process.c    |  5 +++--
 server/protocol.def | 10 ++++++++++
 server/queue.c      |  5 +++--
 server/thread.c     |  5 +++--
 8 files changed, 60 insertions(+), 23 deletions(-)

diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 5d8dbac2df4..dcb7801d912 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -80,17 +80,9 @@ void __wine_esync_set_queue_fd( int fd )
     ntdll_get_thread_data()->esync_queue_fd = fd;
 }
 
-enum esync_type
-{
-    ESYNC_SEMAPHORE = 1,
-    ESYNC_AUTO_EVENT,
-    ESYNC_MANUAL_EVENT,
-    ESYNC_MANUAL_SERVER,
-};
-
 struct esync
 {
-    enum esync_type type;
+    enum esync_type type;   /* defined in protocol.def */
     int fd;
 };
 
@@ -164,6 +156,7 @@ static void *esync_get_object( HANDLE handle )
 static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
 {
     obj_handle_t fd_handle;
+    enum esync_type type;
     struct esync *esync;
     sigset_t sigset;
     NTSTATUS ret;
@@ -180,6 +173,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
             req->handle = wine_server_obj_handle( handle );
             if (!(ret = wine_server_call( req )))
             {
+                type = reply->type;
                 fd = receive_fd( &fd_handle );
                 assert( wine_server_ptr_handle(fd_handle) == handle );
             }
@@ -206,7 +200,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
 
     esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
     esync->fd = fd;
-    esync->type = ESYNC_MANUAL_SERVER;
+    esync->type = type;
 
     add_to_list( handle, esync );
 
@@ -463,8 +457,10 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
                              BOOLEAN alertable, const LARGE_INTEGER *timeout )
 {
     struct esync *objs[MAXIMUM_WAIT_OBJECTS];
-    struct pollfd fds[MAXIMUM_WAIT_OBJECTS];
+    struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 1];
     int has_esync = 0, has_server = 0;
+    DWORD pollcount = count;
+    BOOL msgwait = FALSE;
     LONGLONG timeleft;
     LARGE_INTEGER now;
     ULONGLONG end;
@@ -493,6 +489,15 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
             return ret;
     }
 
+    if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE)
+    {
+        /* Last object in the list is a queue, which means someone is using
+         * MsgWaitForMultipleObjects(). We have to wait not only for the server
+         * fd (signaled on send_message, etc.) but also the USER driver's fd
+         * (signaled on e.g. X11 events.) */
+        msgwait = TRUE;
+    }
+
     if (has_esync && has_server)
     {
         FIXME("Can't wait on esync and server objects at the same time!\n");
@@ -510,6 +515,9 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
         for (i = 0; i < count; i++)
             TRACE(" %p", handles[i]);
 
+        if (msgwait)
+            TRACE(" or driver events (fd %d)", ntdll_get_thread_data()->esync_queue_fd);
+
         if (!timeout)
             TRACE(", timeout = INFINITE.\n");
         else
@@ -527,10 +535,16 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
             fds[i].fd = objs[i] ? objs[i]->fd : -1;
             fds[i].events = POLLIN;
         }
+        if (msgwait)
+        {
+            fds[count].fd = ntdll_get_thread_data()->esync_queue_fd;
+            fds[count].events = POLLIN;
+            pollcount++;
+        }
 
         while (1)
         {
-            ret = do_poll( fds, count, timeout ? &end : NULL );
+            ret = do_poll( fds, pollcount, timeout ? &end : NULL );
             if (ret > 0)
             {
                 /* Find out which object triggered the wait. */
@@ -568,6 +582,12 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
                     }
                 }
 
+                if (msgwait && (fds[count].revents & POLLIN))
+                {
+                    TRACE("Woken up by driver events.\n");
+                    return count - 1;
+                }
+
                 /* If we got here, someone else stole (or reset, etc.) whatever
                  * we were waiting for. So keep waiting. */
                 NtQuerySystemTime( &now );
diff --git a/server/esync.c b/server/esync.c
index 4f7ff6bdaed..1f8c0d5167a 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -150,11 +150,12 @@ int esync_create_fd( int initval, int flags )
 void esync_wake_up( struct object *obj )
 {
     static const uint64_t value = 1;
+    enum esync_type dummy;
     int fd;
 
     if (obj->ops->get_esync_fd)
     {
-        fd = obj->ops->get_esync_fd( obj );
+        fd = obj->ops->get_esync_fd( obj, &dummy );
 
         if (write( fd, &value, sizeof(value) ) == -1)
             perror( "esync: write" );
@@ -219,6 +220,7 @@ DECL_HANDLER(create_esync)
 DECL_HANDLER(get_esync_fd)
 {
     struct object *obj;
+    enum esync_type type;
     int fd;
 
     if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL )))
@@ -226,7 +228,8 @@ DECL_HANDLER(get_esync_fd)
 
     if (obj->ops->get_esync_fd)
     {
-        fd = obj->ops->get_esync_fd( obj );
+        fd = obj->ops->get_esync_fd( obj, &type );
+        reply->type = type;
         send_client_fd( current->process, fd, req->handle );
     }
     else
diff --git a/server/event.c b/server/event.c
index d2f9e378cb1..79287e7edc0 100644
--- a/server/event.c
+++ b/server/event.c
@@ -50,7 +50,7 @@ static void event_dump( struct object *obj, int verbose );
 static struct object_type *event_get_type( struct object *obj );
 static int event_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void event_satisfied( struct object *obj, struct wait_queue_entry *entry );
-static int event_get_esync_fd( struct object *obj );
+static int event_get_esync_fd( struct object *obj, enum esync_type *type );
 static unsigned int event_map_access( struct object *obj, unsigned int access );
 static int event_signal( struct object *obj, unsigned int access);
 static struct list *event_get_kernel_obj_list( struct object *obj );
@@ -203,9 +203,10 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry )
     return event->signaled;
 }
 
-static int event_get_esync_fd( struct object *obj )
+static int event_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct event *event = (struct event *)obj;
+    *type = ESYNC_MANUAL_SERVER;    /* all server-created events are manual-reset */
     return event->esync_fd;
 }
 
diff --git a/server/object.h b/server/object.h
index ee0f1f776e3..ca5a191f975 100644
--- a/server/object.h
+++ b/server/object.h
@@ -69,7 +69,7 @@ struct object_ops
     /* is object signaled? */
     int  (*signaled)(struct object *,struct wait_queue_entry *);
     /* return the esync fd for this object */
-    int (*get_esync_fd)(struct object *);
+    int (*get_esync_fd)(struct object *, enum esync_type *type);
     /* wait satisfied */
     void (*satisfied)(struct object *,struct wait_queue_entry *);
     /* signal an object */
diff --git a/server/process.c b/server/process.c
index ed03210a5f5..7fdbfb98cae 100644
--- a/server/process.c
+++ b/server/process.c
@@ -68,7 +68,7 @@ static struct security_descriptor *process_get_sd( struct object *obj );
 static void process_poll_event( struct fd *fd, int event );
 static struct list *process_get_kernel_obj_list( struct object *obj );
 static void process_destroy( struct object *obj );
-static int process_get_esync_fd( struct object *obj );
+static int process_get_esync_fd( struct object *obj, enum esync_type *type );
 static void terminate_process( struct process *process, struct thread *skip, int exit_code );
 
 static const struct object_ops process_ops =
@@ -656,9 +656,10 @@ static int process_signaled( struct object *obj, struct wait_queue_entry *entry
     return !process->running_threads;
 }
 
-static int process_get_esync_fd( struct object *obj )
+static int process_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct process *process = (struct process *)obj;
+    *type = ESYNC_MANUAL_SERVER;
     return process->esync_fd;
 }
 
diff --git a/server/protocol.def b/server/protocol.def
index 7a383590c55..7644a25b8ec 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4011,4 +4011,14 @@ struct handle_info
 @REQ(get_esync_fd)
     obj_handle_t handle;        /* handle to the object */
 @REPLY
+    int          type;          /* esync type (defined below) */
 @END
+
+enum esync_type
+{
+    ESYNC_SEMAPHORE = 1,
+    ESYNC_AUTO_EVENT,
+    ESYNC_MANUAL_EVENT,
+    ESYNC_MANUAL_SERVER,
+    ESYNC_QUEUE,
+};
diff --git a/server/queue.c b/server/queue.c
index 0ea95291c34..91de4a3a85f 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -165,7 +165,7 @@ static void msg_queue_dump( struct object *obj, int verbose );
 static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry );
 static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry );
 static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int msg_queue_get_esync_fd( struct object *obj );
+static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type );
 static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static void msg_queue_destroy( struct object *obj );
 static void msg_queue_poll_event( struct fd *fd, int event );
@@ -1040,9 +1040,10 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr
     return ret || is_signaled( queue );
 }
 
-static int msg_queue_get_esync_fd( struct object *obj )
+static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct msg_queue *queue = (struct msg_queue *)obj;
+    *type = ESYNC_QUEUE;
     return queue->esync_fd;
 }
 
diff --git a/server/thread.c b/server/thread.c
index 83781f0d712..ae2112486b2 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -133,7 +133,7 @@ static const struct object_ops thread_apc_ops =
 static void dump_thread( struct object *obj, int verbose );
 static struct object_type *thread_get_type( struct object *obj );
 static int thread_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int thread_get_esync_fd( struct object *obj );
+static int thread_get_esync_fd( struct object *obj, enum esync_type *type );
 static unsigned int thread_map_access( struct object *obj, unsigned int access );
 static void thread_poll_event( struct fd *fd, int event );
 static struct list *thread_get_kernel_obj_list( struct object *obj );
@@ -405,9 +405,10 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
     return mythread->state == TERMINATED && !mythread->exit_poll;
 }
 
-static int thread_get_esync_fd( struct object *obj )
+static int thread_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct thread *thread = (struct thread *)obj;
+    *type = ESYNC_MANUAL_SERVER;
     return thread->esync_fd;
 }
 
-- 
2.20.1

