[PATCH 4/4] server: Ensure completion port wait object exists after successful wait.

Paul Gofman pgofman at codeweavers.com
Tue Dec 7 04:59:17 CST 2021


Based on the problem analysis by Andrew Eikum.

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
    Supersedes 220433-220435.

    The added 'waited' request parameter is not needed for NtRemoveIoCompletion[Ex] logic
    but the application may wait on completion port directly.

 dlls/ntdll/unix/sync.c |  6 ++++++
 server/completion.c    | 36 ++++++++++++++++++++++++++++++------
 server/protocol.def    |  1 +
 server/thread.c        |  2 ++
 server/thread.h        |  1 +
 5 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index d5d92145503..bc6a06726b0 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -1815,6 +1815,7 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *
                                       IO_STATUS_BLOCK *io, LARGE_INTEGER *timeout )
 {
     NTSTATUS status;
+    int waited = 0;
 
     TRACE( "(%p, %p, %p, %p, %p)\n", handle, key, value, io, timeout );
 
@@ -1823,6 +1824,7 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *
         SERVER_START_REQ( remove_completion )
         {
             req->handle = wine_server_obj_handle( handle );
+            req->waited = waited;
             if (!(status = wine_server_call( req )))
             {
                 *key            = reply->ckey;
@@ -1835,6 +1837,7 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *
         if (status != STATUS_PENDING) return status;
         status = NtWaitForSingleObject( handle, FALSE, timeout );
         if (status != WAIT_OBJECT_0) return status;
+        waited = 1;
     }
 }
 
@@ -1846,6 +1849,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM
                                         ULONG *written, LARGE_INTEGER *timeout, BOOLEAN alertable )
 {
     NTSTATUS status;
+    int waited = 0;
     ULONG i = 0;
 
     TRACE( "%p %p %u %p %p %u\n", handle, info, count, written, timeout, alertable );
@@ -1857,6 +1861,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM
             SERVER_START_REQ( remove_completion )
             {
                 req->handle = wine_server_obj_handle( handle );
+                req->waited = waited;
                 if (!(status = wine_server_call( req )))
                 {
                     info[i].CompletionKey             = reply->ckey;
@@ -1876,6 +1881,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM
         }
         status = NtWaitForSingleObject( handle, alertable, timeout );
         if (status != WAIT_OBJECT_0) break;
+        waited = 1;
     }
     *written = i ? i : 1;
     return status;
diff --git a/server/completion.c b/server/completion.c
index 27a7f3e3e50..12948277d79 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -169,9 +169,16 @@ static int completion_wait_signaled( struct object *obj, struct wait_queue_entry
 static void completion_wait_satisfied( struct object *obj, struct wait_queue_entry *entry )
 {
     struct completion_wait *wait = (struct completion_wait *)obj;
+    struct thread *thread;
 
     assert( obj->ops == &completion_wait_ops );
-    if (!wait->completion) make_wait_abandoned( entry );
+    if (wait->completion)
+    {
+        thread = get_wait_queue_thread( entry );
+        if (thread->locked_completion) release_object( thread->locked_completion );
+        thread->locked_completion = grab_object( obj );
+    }
+    else make_wait_abandoned( entry );
 }
 
 static void completion_dump( struct object *obj, int verbose )
@@ -297,19 +304,36 @@ DECL_HANDLER(add_completion)
 /* get completion from completion port */
 DECL_HANDLER(remove_completion)
 {
-    struct completion* completion = get_completion_obj( current->process, req->handle, IO_COMPLETION_MODIFY_STATE );
+    struct completion* completion;
+    struct completion_wait *wait;
     struct list *entry;
     struct comp_msg *msg;
 
-    if (!completion) return;
+    if (req->waited && (wait = (struct completion_wait *)current->locked_completion))
+        current->locked_completion = NULL;
+    else
+    {
+        if (current->locked_completion)
+        {
+            release_object( current->locked_completion );
+            current->locked_completion = NULL;
+        }
+        completion = get_completion_obj( current->process, req->handle, IO_COMPLETION_MODIFY_STATE );
+        if (!completion) return;
+
+        wait = (struct completion_wait *)grab_object( completion->wait );
+        release_object( completion );
+    }
 
-    entry = list_head( &completion->wait->queue );
+    assert( wait->obj.ops == &completion_wait_ops );
+
+    entry = list_head( &wait->queue );
     if (!entry)
         set_error( STATUS_PENDING );
     else
     {
         list_remove( entry );
-        completion->wait->depth--;
+        wait->depth--;
         msg = LIST_ENTRY( entry, struct comp_msg, queue_entry );
         reply->ckey = msg->ckey;
         reply->cvalue = msg->cvalue;
@@ -318,7 +342,7 @@ DECL_HANDLER(remove_completion)
         free( msg );
     }
 
-    release_object( completion );
+    release_object( wait );
 }
 
 /* get queue depth for completion port */
diff --git a/server/protocol.def b/server/protocol.def
index efe0d22cbc4..d2c42d595f7 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3501,6 +3501,7 @@ struct handle_info
 /* get completion from completion port queue */
 @REQ(remove_completion)
     obj_handle_t handle;          /* port handle */
+    int          waited;          /* port was just successfully waited on */
 @REPLY
     apc_param_t   ckey;           /* completion key */
     apc_param_t   cvalue;         /* completion value */
diff --git a/server/thread.c b/server/thread.c
index e9240a05659..146bf0e1bae 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -250,6 +250,7 @@ static inline void init_thread_structure( struct thread *thread )
 
     thread->creation_time = current_time;
     thread->exit_time     = 0;
+    thread->locked_completion = NULL;
 
     list_init( &thread->mutex_list );
     list_init( &thread->system_apc );
@@ -452,6 +453,7 @@ static void destroy_thread( struct object *obj )
     release_object( thread->process );
     if (thread->id) free_ptid( thread->id );
     if (thread->token) release_object( thread->token );
+    if (thread->locked_completion) release_object( thread->locked_completion );
 }
 
 /* dump a thread on stdout for debugging purposes */
diff --git a/server/thread.h b/server/thread.h
index 8dcf966a90a..e9442b59d9a 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -90,6 +90,7 @@ struct thread
     struct list            kernel_object; /* list of kernel object pointers */
     data_size_t            desc_len;      /* thread description length in bytes */
     WCHAR                 *desc;          /* thread description string */
+    struct object         *locked_completion; /* completion port wait object successfully waited by the thread */
 };
 
 extern struct thread *current;
-- 
2.33.1




More information about the wine-devel mailing list