[PATCH v2 2/2] ntdll: sock_recv keeps message ordered
Dongwan Kim
kdw6485 at gmail.com
Mon Dec 6 19:30:47 CST 2021
Calling try_recv could make messages misordered.
Suppose that a server program calls a overlapped WSARecv
and its operation is pending.
Another program sends a message and wineserver is busy.
The server program calls another overlapped WSARecv and
it intercepts the message for the pending WSARecv.
The problem already had been discussed here
https://www.winehq.org/pipermail/wine-devel/2021-May/186612.html
To avoid this situation, this kind of approach can be applied.
The server program needs to know if there are pending asyncs
before calling try_recv.
Wineserver notifies it whenever invoking APC_ASYNC_IO.
Then, the server program updates it and check it before calling try_recv.
Signed-off-by: Dongwan Kim <kdw6485 at gmail.com>
---
dlls/ntdll/unix/server.c | 8 +++----
dlls/ntdll/unix/socket.c | 42 +++++++++++++++++++++++++++++++++-
include/wine/server_protocol.h | 1 +
server/async.c | 8 +++++++
4 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index 1dcd0792072..1144bbcfa11 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -374,17 +374,17 @@ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOO
case APC_ASYNC_IO:
{
struct async_fileio *user = wine_server_get_ptr( call->async_io.user );
- ULONG_PTR info = call->async_io.result;
+ ULONG_PTR info[2] = { call->async_io.result, call->async_io.async_wait };
NTSTATUS status;
result->type = call->type;
status = call->async_io.status;
- if (user->callback( user, &info, &status ))
+ if (user->callback( user, info, &status ))
{
result->async_io.status = status;
- result->async_io.total = info;
+ result->async_io.total = *info;
/* the server will pass us NULL if a call failed synchronously */
- set_async_iosb( call->async_io.sb, result->async_io.status, info );
+ set_async_iosb( call->async_io.sb, result->async_io.status, *info );
}
else result->async_io.status = STATUS_PENDING; /* restart it */
break;
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 2e79b9baa0f..ca56a7707a6 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -575,13 +575,45 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
return status;
}
+struct async_queue{
+ struct list entry;
+ HANDLE sock;
+ char read_q;
+ char write_q;
+};
+static struct list async_queue_list = LIST_INIT(async_queue_list);
+
+
+static struct async_queue* alloc_async_queue(HANDLE handle){
+ struct async_queue *queue =
+ (struct async_queue*)malloc(sizeof(struct async_queue));
+ memset(queue, 0, sizeof(struct async_queue));
+
+ queue->sock = handle;
+ list_add_head(&async_queue_list, &queue->entry);
+ return queue;
+}
+
+static struct async_queue* find_async_queue(HANDLE handle){
+ struct async_queue *queue;
+
+ LIST_FOR_EACH_ENTRY(queue, &async_queue_list, struct async_queue, entry){
+ if(queue->sock == handle)
+ return queue;
+ }
+ return NULL;
+}
static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
{
struct async_recv_ioctl *async = user;
+ struct async_queue *queue;
int fd, needs_close;
TRACE( "%#x\n", *status );
+ queue = find_async_queue(async->io.handle);
+ if(queue)
+ queue->read_q = info[1]; //store if waiting asyncs exist
if (*status == STATUS_ALERTED)
{
if ((*status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL )))
@@ -603,6 +635,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
struct WS_sockaddr *addr, int *addr_len, DWORD *ret_flags, int unix_flags, int force_async )
{
struct async_recv_ioctl *async;
+ struct async_queue *queue;
ULONG_PTR information;
HANDLE wait_handle;
DWORD async_size;
@@ -641,7 +674,13 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
async->addr_len = addr_len;
async->ret_flags = ret_flags;
- status = try_recv( fd, async, &information );
+ queue = find_async_queue(handle);
+ if(!queue)
+ queue = alloc_async_queue(handle);
+ if(queue->read_q ) // if there are waiting asyncs, avoid cutting in line.
+ status = STATUS_DEVICE_NOT_READY;
+ else
+ status = try_recv( fd, async, &information );
if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW && status != STATUS_DEVICE_NOT_READY)
{
@@ -670,6 +709,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
SERVER_END_REQ;
if (status != STATUS_PENDING) release_fileio( &async->io );
+ else queue->read_q = 1;
if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT );
return status;
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index b37a8e8e056..4dc2eb567db 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -492,6 +492,7 @@ typedef union
client_ptr_t user;
client_ptr_t sb;
data_size_t result;
+ unsigned int async_wait;
} async_io;
struct
{
diff --git a/server/async.c b/server/async.c
index ec3396c930f..0220bf4e2f4 100644
--- a/server/async.c
+++ b/server/async.c
@@ -164,6 +164,7 @@ static void async_destroy( struct object *obj )
void async_terminate( struct async *async, unsigned int status )
{
struct iosb *iosb = async->iosb;
+ struct async* async_waiting;
if (async->terminated) return;
@@ -202,6 +203,13 @@ void async_terminate( struct async *async, unsigned int status )
else
data.async_io.status = status;
+ async_waiting = find_pending_async(async->queue);
+ if(async_waiting)
+ {
+ data.async_io.async_wait = 1;
+ release_object(async_waiting);
+ }
+
thread_queue_apc( async->thread->process, async->thread, &async->obj, &data );
}
--
2.30.2
More information about the wine-devel
mailing list