From 165666d8b8b1b21ffaf43ac436cfc1657ba83649 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 30 Jul 2018 18:22:46 +0200 Subject: [socket-nif] Add support for recvmsg Added preliminary support for function recvmsg. At the moment this only works on *nix (Windows has another function, WSARecvMsg, which has a slightly different API). Also we have "no" cmsg decode at the moment (just level and type). OTP-14831 --- erts/preloaded/ebin/socket.beam | Bin 59768 -> 61376 bytes erts/preloaded/src/socket.erl | 80 ++++++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 11 deletions(-) (limited to 'erts/preloaded') diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam index b2bd8f2728..f6ca653fed 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 1983c993a5..b9d1705d45 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -43,7 +43,7 @@ recv/1, recv/2, recv/3, recv/4, recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4, - %% recvmsg/4, + recvmsg/1, recvmsg/2, recvmsg/5, %% readv/3, close/1, @@ -500,24 +500,28 @@ -type shutdown_how() :: read | write | read_write. %% These are just place-holder(s) - used by the sendmsg/recvmsg functions... --type msghdr_flag() :: eor | trunc | ctrunc | oob | errqueue. +-type msghdr_flag() :: ctrunc | eor | errqueue | oob | trunc. -type msghdr_flags() :: [msghdr_flag()]. -type msghdr() :: #{ %% *Optional* target address %% *If* this field is specified for an unconnected %% socket, then it will be used as destination for the %% datagram. - target => sockaddr(), + addr => sockaddr(), - iov => [binary()], + iov => [binary()], - ctrl => cmsghdr(), + ctrl => [cmsghdr()], %% Only valid with recvmsg flags => msghdr_flags() }. +%% At some point we should be able to encode/decode the most common types +%% of control message headers. For now, we leave/take the data part raw +%% (as a binary) and leave it to the user to figure out (how to encode/decode +%% that bit). -type cmsghdr() :: #{ - level => protocol(), + level => protocol() | integer(), type => integer(), data => binary() }. @@ -1752,11 +1756,62 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -%% -spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when -%% Socket :: socket(), -%% MsgHdr :: msghdr(), -%% Flags :: recv_flags(), -%% Reason :: term(). +recvmsg(Socket) -> + recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). + +recvmsg(Socket, Flags) when is_list(Flags) -> + recvmsg(Socket, 0, 0, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); +recvmsg(Socket, Timeout) -> + recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). + +-spec recvmsg(Socket, + BufSz, CtrlSz, + Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Flags :: recv_flags(), + Timeout :: timeout(), + MsgHdr :: msghdr(), + Reason :: term(). + +recvmsg(#socket{ref = SockRef}, BufSz, CtrlSz, Flags, Timeout) + when (is_integer(BufSz) andalso (BufSz >= 0)) andalso + (is_integer(CtrlSz) andalso (CtrlSz >= 0)) andalso + is_list(Flags) andalso + (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + EFlags = enc_recv_flags(Flags), + do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout). + +do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> + TS = timestamp(Timeout), + RecvRef = make_ref(), + case nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags) of + {ok, _MsgHdr} = OK -> + OK; + + {error, eagain} -> + %% There is nothing just now, but we will be notified when there + %% is something to read (a select message). + NewTimeout = next_timeout(TS, Timeout), + receive + {select, SockRef, RecvRef, ready_input} -> + do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, + next_timeout(TS, Timeout)); + + {nif_abort, RecvRef, Reason} -> + {error, Reason} + + after NewTimeout -> + nif_cancel(SockRef, recvmsg, RecvRef), + flush_select_msgs(SockRef, RecvRef), + {error, timeout} + end; + + {error, _Reason} = ERROR -> + ERROR + + end. @@ -3171,6 +3226,9 @@ nif_recv(_SRef, _RecvRef, _Length, _Flags) -> nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) -> erlang:error(badarg). +nif_recvmsg(_SRef, _RecvRef, _BufSz, _CtrlSz, _Flags) -> + erlang:error(badarg). + nif_cancel(_SRef, _Op, _Ref) -> erlang:error(badarg). -- cgit v1.2.3