diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index b43d7d61867..d37c25f9225 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -3106,6 +3106,29 @@ static test_setup tests [] = } }; +struct send_udp_thread_param +{ + int sock; + HANDLE start_event; +}; + +static DWORD WINAPI send_udp_thread( void *param ) +{ + struct send_udp_thread_param *p = param; + static char buf[256]; + unsigned int i; + int ret; + + WaitForSingleObject( p->start_event, INFINITE ); + for (i = 0; i < 256; ++i) + { + ret = send( p->sock, buf, sizeof(buf), 0 ); + ok( ret == sizeof(buf), "got %d, error %u, i %u.\n", ret, WSAGetLastError(), i ); + } + + return 0; +} + static void test_UDP(void) { /* This function tests UDP sendto() and recvfrom(). UDP is unreliable, so it is @@ -3117,6 +3140,9 @@ static void test_UDP(void) int ss, i, n_recv, n_sent, ret; struct sockaddr_in addr; int sock; + struct send_udp_thread_param udp_thread_param; + HANDLE thread; + memset (buf,0,sizeof(buf)); for ( i = NUM_UDP_PEERS - 1; i >= 0; i-- ) { @@ -3174,6 +3200,22 @@ static void test_UDP(void) ok( ret == sizeof(buf), "got %d, error %u.\n", ret, WSAGetLastError() ); } + /* Test sending packets in parallel (mostly a regression test for Wine async handling race conditions). */ + set_blocking( sock, FALSE ); + + udp_thread_param.sock = sock; + udp_thread_param.start_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + thread = CreateThread( NULL, 0, send_udp_thread, &udp_thread_param, 0, NULL ); + SetEvent( udp_thread_param.start_event ); + for (i = 0; i < 256; ++i) + { + ret = send( sock, buf, sizeof(buf), 0 ); + ok( ret == sizeof(buf), "got %d, error %u, i %u.\n", ret, WSAGetLastError(), i ); + } + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( udp_thread_param.start_event ); + closesocket(sock); } diff --git a/server/async.c b/server/async.c index 9cb251df5ce..80129ac0ac3 100644 --- a/server/async.c +++ b/server/async.c @@ -551,6 +551,16 @@ void async_set_result( struct object *obj, unsigned int status, apc_param_t tota } } +int async_queue_has_waiting_asyncs( struct async_queue *queue ) +{ + struct async *async; + + LIST_FOR_EACH_ENTRY( async, &queue->queue, struct async, queue_entry ) + if (!async->unknown_status) return 1; + + return 0; +} + /* check if an async operation is waiting to be alerted */ int async_waiting( struct async_queue *queue ) { diff --git a/server/file.h b/server/file.h index 39a833cd105..7f2d1637863 100644 --- a/server/file.h +++ b/server/file.h @@ -237,6 +237,7 @@ extern void set_async_pending( struct async *async ); extern void async_set_initial_status( struct async *async, unsigned int status ); extern void async_wake_obj( struct async *async ); extern int async_waiting( struct async_queue *queue ); +extern int async_queue_has_waiting_asyncs( struct async_queue *queue ); extern void async_terminate( struct async *async, unsigned int status ); extern void async_request_complete( struct async *async, unsigned int status, data_size_t result, data_size_t out_size, void *out_data ); diff --git a/server/sock.c b/server/sock.c index c34fd3eb5eb..a884179c604 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3945,7 +3945,7 @@ DECL_HANDLER(send_socket) if (bind_errno) status = sock_get_ntstatus( bind_errno ); else if (sock->wr_shutdown) status = STATUS_PIPE_DISCONNECTED; - else if (!async_queued( &sock->write_q )) + else if (!async_queue_has_waiting_asyncs( &sock->write_q )) { /* If write_q is not empty, we cannot really tell if the already queued * asyncs will not consume all available space; if there's no space