From f18bd2a88d7bc8519cf5db611c4c530eedecba2a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 5 Nov 2018 14:43:07 +0100 Subject: [socket-nif] Add "partial success" to sendmsg The sendmsg function attempts to send *one message*. But its possible for the underlying software to fail to send the *entire* message. So, instead of retrying itself, as send does, the sendmsg function will now instead return with {ok, Remaining}, leaving it to the caller to decide what to do. OTP-14831 --- erts/preloaded/ebin/socket.beam | Bin 68564 -> 69380 bytes erts/preloaded/src/socket.erl | 47 +++++++++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam index 480f86334c..d3bc7c7af0 100644 Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5ebc2074e0..dd10aac3ff 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1573,12 +1573,13 @@ sendmsg(Socket, MsgHdr, Timeout) sendmsg(Socket, MsgHdr, ?SOCKET_SENDMSG_FLAGS_DEFAULT, Timeout). --spec sendmsg(Socket, MsgHdr, Flags, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - MsgHdr :: msghdr(), - Flags :: send_flags(), - Timeout :: timeout(), - Reason :: term(). +-spec sendmsg(Socket, MsgHdr, Flags, Timeout) -> ok | {ok, Remaining} | {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Flags :: send_flags(), + Timeout :: timeout(), + Remaining :: erlang:iovec(), + Reason :: term(). sendmsg(#socket{ref = SockRef}, #{iov := IOV} = MsgHdr, Flags, Timeout) when is_list(IOV) andalso @@ -1603,6 +1604,18 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> %% We are done ok; + {ok, Written} when is_integer(Written) andalso (Written > 0) -> + + %% We should not retry here since the protocol may not + %% be able to handle a message being split. Leave it to + %% the caller to figure out (call again with the rest). + %% + %% We should really not need to cancel, since this is + %% accepted for sendmsg! + %% + cancel(SockRef, sendmsg, SendRef), + {ok, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)}; + {error, eagain} -> receive {select, SockRef, SendRef, ready_output} -> @@ -1617,6 +1630,12 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> ERROR end. +do_sendmsg_rest([B|IOVec], Written) when (Written >= size(B)) -> + do_sendmsg_rest(IOVec, Written - size(B)); +do_sendmsg_rest([B|IOVec], Written) -> + <<_:Written/binary, Rest/binary>> = B, + [Rest|IOVec]. + ensure_msghdr(#{ctrl := []} = M) -> ensure_msghdr(maps:remove(ctrl, M)); ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) -> @@ -1625,6 +1644,8 @@ ensure_msghdr(_) -> einval(). + + %% =========================================================================== %% %% writev - write data into multiple buffers @@ -1983,10 +2004,20 @@ recvmsg(Socket, Timeout) -> Flags :: recv_flags(), Timeout :: timeout(), MsgHdr :: msghdr(), + Reason :: term() + ; (Socket, BufSz, CtrlSz) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + MsgHdr :: msghdr(), Reason :: term(). -recvmsg(Socket, Flags, Timeout) -> - recvmsg(Socket, 0, 0, Flags, Timeout). +recvmsg(Socket, Flags, Timeout) when is_list(Flags) -> + recvmsg(Socket, 0, 0, Flags, Timeout); +recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz) -> + recvmsg(Socket, BufSz, CtrlSz, + ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). + -spec recvmsg(Socket, BufSz, CtrlSz, -- cgit v1.2.3