[PATCH v2 4/7] winhttp: Replace pending read cancel in WinHttpWebSocketClose() with a generic cancel_queue().

Paul Gofman wine at gitlab.winehq.org
Tue Jun 7 12:20:55 CDT 2022


From: Paul Gofman <pgofman at codeweavers.com>

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/winhttp/request.c         | 108 +++++++++++++++++++++++----------
 dlls/winhttp/winhttp_private.h |   3 +-
 2 files changed, 78 insertions(+), 33 deletions(-)

diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index 29887c8d7d0..4242551ee3b 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -182,7 +182,7 @@ static void CALLBACK task_callback( TP_CALLBACK_INSTANCE *instance, void *ctx )
     task = get_next_task( queue, NULL );
     while (task)
     {
-        task->callback( task );
+        task->callback( task, FALSE );
         /* Queue object may be freed by release_object() unless there is another task referencing it. */
         next_task = get_next_task( queue, task );
         release_object( task->obj );
@@ -199,6 +199,7 @@ static DWORD queue_task( struct queue *queue, TASK_CALLBACK task, struct task_he
 
     TRACE("queueing %p in %p\n", task_hdr, queue);
     task_hdr->callback = task;
+    task_hdr->completion_sent = 0;
     task_hdr->refs = 1;
     task_hdr->obj = obj;
     addref_object( obj );
@@ -225,6 +226,35 @@ static DWORD queue_task( struct queue *queue, TASK_CALLBACK task, struct task_he
     return ERROR_SUCCESS;
 }
 
+static BOOL task_needs_completion( struct task_header *task_hdr )
+{
+    return !InterlockedExchange( &task_hdr->completion_sent, 1 );
+}
+
+static void cancel_queue( struct queue *queue )
+{
+    struct task_header *task_hdr, *found;
+
+    while (1)
+    {
+        AcquireSRWLockExclusive( &queue->lock );
+        found = NULL;
+        LIST_FOR_EACH_ENTRY( task_hdr, &queue->queued_tasks, struct task_header, entry )
+        {
+            if (task_needs_completion( task_hdr ))
+            {
+                found = task_hdr;
+                addref_task( found );
+                break;
+            }
+        }
+        ReleaseSRWLockExclusive( &queue->lock );
+        if (!found) break;
+        found->callback( found, TRUE );
+        release_task( found );
+    }
+}
+
 static void free_header( struct header *header )
 {
     free( header->field );
@@ -2231,11 +2261,13 @@ end:
     return ret;
 }
 
-static void task_send_request( void *ctx )
+static void task_send_request( void *ctx, BOOL abort )
 {
     struct send_request *s = ctx;
     struct request *request = (struct request *)s->task_hdr.obj;
 
+    if (abort) return;
+
     TRACE( "running %p\n", ctx );
     send_request( request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
 
@@ -2813,11 +2845,13 @@ static DWORD receive_response( struct request *request, BOOL async )
     return ret;
 }
 
-static void task_receive_response( void *ctx )
+static void task_receive_response( void *ctx, BOOL abort )
 {
     struct receive_response *r = ctx;
     struct request *request = (struct request *)r->task_hdr.obj;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
     receive_response( request, TRUE );
 }
@@ -2905,11 +2939,13 @@ done:
     return ret;
 }
 
-static void task_query_data_available( void *ctx )
+static void task_query_data_available( void *ctx, BOOL abort )
 {
     struct query_data *q = ctx;
     struct request *request = (struct request *)q->task_hdr.obj;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
     query_data_available( request, q->available, TRUE );
 }
@@ -2956,11 +2992,13 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
     return !ret || ret == ERROR_IO_PENDING;
 }
 
-static void task_read_data( void *ctx )
+static void task_read_data( void *ctx, BOOL abort )
 {
     struct read_data *r = ctx;
     struct request *request = (struct request *)r->task_hdr.obj;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
     read_data( request, r->buffer, r->to_read, r->read, TRUE );
 }
@@ -3031,11 +3069,13 @@ static DWORD write_data( struct request *request, const void *buffer, DWORD to_w
     return ret;
 }
 
-static void task_write_data( void *ctx )
+static void task_write_data( void *ctx, BOOL abort )
 {
     struct write_data *w = ctx;
     struct request *request = (struct request *)w->task_hdr.obj;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
     write_data( request, w->buffer, w->to_write, w->written, TRUE );
 }
@@ -3315,13 +3355,10 @@ static void send_io_complete( struct object_header *hdr )
 }
 
 /* returns FALSE if sending callback should be omitted. */
-static BOOL receive_io_complete( struct socket *socket )
+static void receive_io_complete( struct socket *socket )
 {
     LONG count = InterlockedDecrement( &socket->hdr.pending_receives );
-    assert( count >= 0 || socket->state == SOCKET_STATE_CLOSED);
-    /* count is reset to zero during websocket close so if count went negative
-     * then WinHttpWebSocketClose() is to send the callback. */
-    return count >= 0;
+    assert( count >= 0 );
 }
 
 static BOOL socket_can_send( struct socket *socket )
@@ -3428,12 +3465,14 @@ static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE
     return send_frame( socket, opcode, 0, buf, len, final, ovr );
 }
 
-static void task_socket_send( void *ctx )
+static void task_socket_send( void *ctx, BOOL abort )
 {
     struct socket_send *s = ctx;
     struct socket *socket = (struct socket *)s->task_hdr.obj;
     DWORD ret;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
 
     if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->buf );
@@ -3623,11 +3662,13 @@ static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_o
     return ERROR_SUCCESS;
 }
 
-static void task_socket_send_pong( void *ctx )
+static void task_socket_send_pong( void *ctx, BOOL abort )
 {
     struct socket_send *s = ctx;
     struct socket *socket = (struct socket *)s->task_hdr.obj;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
 
     if (s->complete_async) complete_send_frame( socket, &s->ovr, NULL );
@@ -3858,17 +3899,23 @@ static void socket_receive_complete( struct socket *socket, DWORD ret, WINHTTP_W
     }
 }
 
-static void task_socket_receive( void *ctx )
+static void task_socket_receive( void *ctx, BOOL abort )
 {
     struct socket_receive *r = ctx;
     struct socket *socket = (struct socket *)r->task_hdr.obj;
     DWORD ret, count;
     WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
 
+    if (abort)
+    {
+        socket_receive_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED, 0, 0 );
+        return;
+    }
+
     TRACE("running %p\n", ctx);
     ret = socket_receive( socket, r->buf, r->len, &count, &type );
-
-    if (receive_io_complete( socket ))
+    receive_io_complete( socket );
+    if (task_needs_completion( &r->task_hdr ))
         socket_receive_complete( socket, ret, type, count );
 }
 
@@ -3939,12 +3986,14 @@ static void socket_shutdown_complete( struct socket *socket, DWORD ret )
     }
 }
 
-static void task_socket_shutdown( void *ctx )
+static void task_socket_shutdown( void *ctx, BOOL abort )
 {
     struct socket_shutdown *s = ctx;
     struct socket *socket = (struct socket *)s->task_hdr.obj;
     DWORD ret;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
 
     if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->reason );
@@ -4073,15 +4122,18 @@ static void socket_close_complete( struct socket *socket, DWORD ret )
     }
 }
 
-static void task_socket_close( void *ctx )
+static void task_socket_close( void *ctx, BOOL abort )
 {
     struct socket_shutdown *s = ctx;
     struct socket *socket = (struct socket *)s->task_hdr.obj;
     DWORD ret;
 
+    if (abort) return;
+
     TRACE("running %p\n", ctx);
 
     ret = socket_close( socket );
+    receive_io_complete( socket );
     socket_close_complete( socket, ret );
 }
 
@@ -4113,25 +4165,14 @@ DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reas
 
     if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
     {
-        /* When closing the socket pending receives are cancelled. Setting socket->hdr.pending_receives to zero
-         * will prevent pending receives from sending callbacks. */
-        pending_receives = InterlockedExchange( &socket->hdr.pending_receives, 0 );
-        assert( pending_receives >= 0 );
-        if (pending_receives)
-        {
-            WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
-
-            result.AsyncResult.dwResult = 0;
-            result.AsyncResult.dwError = ERROR_WINHTTP_OPERATION_CANCELLED;
-            result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
-            send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
-        }
+        pending_receives = InterlockedIncrement( &socket->hdr.pending_receives );
+        cancel_queue( &socket->recv_q );
     }
 
     if (prev_state < SOCKET_STATE_SHUTDOWN
         && (ret = send_socket_shutdown( socket, status, reason, len, FALSE ))) goto done;
 
-    if (!pending_receives && socket->close_frame_received)
+    if (pending_receives == 1 && socket->close_frame_received)
     {
         if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
             socket_close_complete( socket, socket->close_frame_receive_err );
@@ -4144,7 +4185,10 @@ DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reas
 
         if (!(s = calloc( 1, sizeof(*s) ))) return FALSE;
         if ((ret = queue_task( &socket->recv_q, task_socket_close, &s->task_hdr, &socket->hdr )))
+        {
+            InterlockedDecrement( &socket->hdr.pending_receives );
             free( s );
+        }
     }
     else ret = socket_close( socket );
 
diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h
index ee3cdb78157..17102107c77 100644
--- a/dlls/winhttp/winhttp_private.h
+++ b/dlls/winhttp/winhttp_private.h
@@ -274,7 +274,7 @@ struct socket
     BOOL last_receive_final;
 };
 
-typedef void (*TASK_CALLBACK)( void *ctx );
+typedef void (*TASK_CALLBACK)( void *ctx, BOOL abort );
 
 struct task_header
 {
@@ -282,6 +282,7 @@ struct task_header
     TASK_CALLBACK callback;
     struct object_header *obj;
     volatile LONG refs;
+    volatile LONG completion_sent;
 };
 
 struct send_request
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/195



More information about the wine-devel mailing list