[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