This allows the client to postpone the initial I/O until the server has queued the I/O request. The server should perform the postprocessing only after the initial I/O has been done.
In the case of send_socket, the manipulation of event flags shall ideally be done *after* (not *before*) the client has attempted the initial I/O, since the outbound queue status of the socket may change in the meanwhile. Also, the implicitly bound address is available only after the send* system call has been performed.
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: v1 -> v2: - pass around total size of data to be transmitted - detect short write in send_socket_initial_callback v2 -> v3: no changes
dlls/ntdll/unix/socket.c | 15 ++++++++++++++ server/protocol.def | 1 + server/sock.c | 42 +++++++++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 23059e3cff8..d04ebb7f874 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -873,6 +873,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi NTSTATUS status; unsigned int i; ULONG options; + data_size_t data_size;
async_size = offsetof( struct async_send_ioctl, iov[count] );
@@ -906,6 +907,18 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi async->iov_cursor = 0; async->sent_len = 0;
+ data_size = 0; + for (i = 0; i < count; ++i) + { + SIZE_T len = async->iov[i].iov_len; + if (len > (SIZE_T)(data_size_t)-1 || (data_size_t)(data_size + len) < data_size) + { + release_fileio( &async->io ); + return STATUS_NO_MEMORY; + } + data_size += len; + } + status = try_send( fd, async );
if (status != STATUS_SUCCESS && status != STATUS_DEVICE_NOT_READY) @@ -919,6 +932,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
SERVER_START_REQ( send_socket ) { + req->data_size = data_size; req->status = status; req->total = async->sent_len; req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); @@ -1099,6 +1113,7 @@ static NTSTATUS sock_transmit( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc,
SERVER_START_REQ( send_socket ) { + req->data_size = async->head_len + async->file_len + async->tail_len; req->status = STATUS_PENDING; req->total = 0; req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); diff --git a/server/protocol.def b/server/protocol.def index 9d90544fa41..2e57c91ccad 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1461,6 +1461,7 @@ enum server_fd_type
/* Perform a send on a socket */ @REQ(send_socket) + data_size_t data_size; /* total number of bytes to send (>= total) */ async_data_t async; /* async I/O parameters */ unsigned int status; /* status of initial call */ unsigned int total; /* number of bytes already sent */ diff --git a/server/sock.c b/server/sock.c index f6a65e4e084..8653492ce34 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3460,16 +3460,17 @@ DECL_HANDLER(recv_socket) release_object( sock ); }
-DECL_HANDLER(send_socket) +static void send_socket_initial_callback( void *private, struct async *async, struct fd *fd, unsigned int status ) { - struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops ); - unsigned int status = req->status; - timeout_t timeout = 0; - struct async *async; - struct fd *fd; + struct sock *sock = get_fd_user( fd ); + struct iosb *iosb; + int is_short_write = 0;
- if (!sock) return; - fd = sock->fd; + if ((iosb = async_get_iosb( async ))) + { + is_short_write = iosb->result < (unsigned long)private; + release_object( iosb ); + }
if (sock->type == WS_SOCK_DGRAM) { @@ -3482,13 +3483,29 @@ DECL_HANDLER(send_socket) sock->bound = 1; }
- if (status != STATUS_SUCCESS) + if (status != STATUS_SUCCESS || is_short_write) { - /* send() calls only clear and reselect events if unsuccessful. */ + /* send() calls only clear and reselect events if unsuccessful. + * Also treat short writes as being unsuccessful. + */ sock->pending_events &= ~AFD_POLL_WRITE; sock->reported_events &= ~AFD_POLL_WRITE; }
+ sock_reselect( sock ); +} + +DECL_HANDLER(send_socket) +{ + struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops ); + unsigned int status = req->status; + timeout_t timeout = 0; + struct async *async; + struct fd *fd; + + if (!sock) return; + fd = sock->fd; + /* If we had a short write and the socket is nonblocking (and the client is * not trying to force the operation to be asynchronous), return success. * Windows actually refuses to send any data in this case, and returns @@ -3524,15 +3541,14 @@ DECL_HANDLER(send_socket) } set_error( status );
+ async_set_initial_status_callback( async, send_socket_initial_callback, (void *)(unsigned long)req->data_size ); + if (timeout) async_set_timeout( async, timeout, STATUS_IO_TIMEOUT );
if (status == STATUS_PENDING) queue_async( &sock->write_q, async );
- /* always reselect; we changed reported_events above */ - sock_reselect( sock ); - reply->wait = async_handoff( async, NULL, 0 ); reply->options = get_fd_options( fd ); release_object( async );