aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-09-28 18:40:08 +0200
committerMicael Karlberg <[email protected]>2018-09-28 18:40:08 +0200
commit1c412c62ba3be17b7a818f264049a7ee7942351e (patch)
tree675494d373a04c6b15ff0fc19b24725d5f95c986
parent0b56a98366fc152c0fa5d5398220ac31866114d5 (diff)
downloadotp-1c412c62ba3be17b7a818f264049a7ee7942351e.tar.gz
otp-1c412c62ba3be17b7a818f264049a7ee7942351e.tar.bz2
otp-1c412c62ba3be17b7a818f264049a7ee7942351e.zip
[socket-nif] Add support for socket (level otp) buffer options
Add support for otp level socket options rcvbuf, rcvctrlbuf and sndctrlbuf. These options define default sizes for these buffers. The 'rcvbuf' is used when receiving messages when calling the recv, recvfrom and recvmsg functions. The 'rcvctrlbuf' is used for the control message header info when calling the recvmsg function. The 'sndctrlbuf' is used for the control message header info when calling the sendmsg function. OTP-14831
-rw-r--r--erts/doc/src/socket_usage.xml21
-rw-r--r--erts/emulator/nifs/common/socket_nif.c146
-rw-r--r--erts/emulator/nifs/common/socket_util.c29
-rw-r--r--erts/emulator/nifs/common/socket_util.h6
-rw-r--r--erts/preloaded/ebin/socket.beambin66040 -> 66584 bytes
-rw-r--r--erts/preloaded/src/socket.erl33
-rw-r--r--lib/kernel/test/socket_SUITE.erl112
7 files changed, 342 insertions, 5 deletions
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index 9785830637..23d0f319f1 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -81,6 +81,27 @@
<cell>yes</cell>
<cell>none</cell>
</row>
+ <row>
+ <cell>rcvbuf</cell>
+ <cell>default | pos_integer()</cell>
+ <cell>yes</cell>
+ <cell>yes</cell>
+ <cell>default only valid for set</cell>
+ </row>
+ <row>
+ <cell>rcvctrlbuf</cell>
+ <cell>default | pos_integer()</cell>
+ <cell>yes</cell>
+ <cell>yes</cell>
+ <cell>default only valid for set</cell>
+ </row>
+ <row>
+ <cell>sndctrlbuf</cell>
+ <cell>default | pos_integer()</cell>
+ <cell>yes</cell>
+ <cell>yes</cell>
+ <cell>default only valid for set</cell>
+ </row>
</table>
<p>Options for level <c>socket</c>: </p>
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index ccbcf63ece..876bed3135 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -501,6 +501,9 @@ typedef union {
#define SOCKET_OPT_OTP_DEBUG 1
#define SOCKET_OPT_OTP_IOW 2
#define SOCKET_OPT_OTP_CTRL_PROC 3
+#define SOCKET_OPT_OTP_RCVBUF 4
+#define SOCKET_OPT_OTP_RCVCTRLBUF 6
+#define SOCKET_OPT_OTP_SNDCTRLBUF 7
#define SOCKET_OPT_SOCK_ACCEPTCONN 1
#define SOCKET_OPT_SOCK_BINDTODEVICE 3
@@ -1004,6 +1007,15 @@ static ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env,
static ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env,
SocketDescriptor* descP,
ERL_NIF_TERM eVal);
+static ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ ERL_NIF_TERM eVal);
+static ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ ERL_NIF_TERM eVal);
+static ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ ERL_NIF_TERM eVal);
static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env,
SocketDescriptor* descP,
int level,
@@ -1492,6 +1504,12 @@ static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env,
SocketDescriptor* descP);
static ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env,
SocketDescriptor* descP);
+static ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env,
+ SocketDescriptor* descP);
+static ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP);
+static ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP);
static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
SocketDescriptor* descP,
int level,
@@ -5001,6 +5019,18 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env,
result = nsetopt_otp_ctrl_proc(env, descP, eVal);
break;
+ case SOCKET_OPT_OTP_RCVBUF:
+ result = nsetopt_otp_rcvbuf(env, descP, eVal);
+ break;
+
+ case SOCKET_OPT_OTP_RCVCTRLBUF:
+ result = nsetopt_otp_rcvctrlbuf(env, descP, eVal);
+ break;
+
+ case SOCKET_OPT_OTP_SNDCTRLBUF:
+ result = nsetopt_otp_sndctrlbuf(env, descP, eVal);
+ break;
+
default:
result = esock_make_error(env, esock_atom_einval);
break;
@@ -5079,6 +5109,74 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env,
+/* nsetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
+ */
+static
+ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ ERL_NIF_TERM eVal)
+{
+ size_t val;
+ char* xres;
+
+ if ((xres = esock_decode_bufsz(env,
+ eVal,
+ SOCKET_RECV_BUFFER_SIZE_DEFAULT, &val)) != NULL)
+ return esock_make_error_str(env, xres);
+
+ descP->rBufSz = val;
+
+ return esock_atom_ok;
+}
+
+
+
+/* nsetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
+ */
+static
+ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ ERL_NIF_TERM eVal)
+{
+ size_t val;
+ char* xres;
+
+ if ((xres = esock_decode_bufsz(env,
+ eVal,
+ SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT,
+ &val)) != NULL)
+ return esock_make_error_str(env, xres);
+
+ descP->rCtrlSz = val;
+
+ return esock_atom_ok;
+}
+
+
+
+/* nsetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
+ */
+static
+ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP,
+ ERL_NIF_TERM eVal)
+{
+ size_t val;
+ char* xres;
+
+ if ((xres = esock_decode_bufsz(env,
+ eVal,
+ SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT,
+ &val)) != NULL)
+ return esock_make_error_str(env, xres);
+
+ descP->wCtrlSz = val;
+
+ return esock_atom_ok;
+}
+
+
+
/* The option has *not* been encoded. Instead it has been provided
* in "native mode" (option is provided as is and value as a binary).
*/
@@ -8169,6 +8267,18 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env,
result = ngetopt_otp_ctrl_proc(env, descP);
break;
+ case SOCKET_OPT_OTP_RCVBUF:
+ result = ngetopt_otp_rcvbuf(env, descP);
+ break;
+
+ case SOCKET_OPT_OTP_RCVCTRLBUF:
+ result = ngetopt_otp_rcvctrlbuf(env, descP);
+ break;
+
+ case SOCKET_OPT_OTP_SNDCTRLBUF:
+ result = ngetopt_otp_sndctrlbuf(env, descP);
+ break;
+
default:
result = esock_make_error(env, esock_atom_einval);
break;
@@ -8220,6 +8330,42 @@ ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env,
+/* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf options
+ */
+static
+ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ ERL_NIF_TERM eVal = MKI(env, descP->rBufSz);
+
+ return esock_make_ok2(env, eVal);
+}
+
+
+/* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf options
+ */
+static
+ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ ERL_NIF_TERM eVal = MKI(env, descP->rCtrlSz);
+
+ return esock_make_ok2(env, eVal);
+}
+
+
+/* ngetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf options
+ */
+static
+ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ ERL_NIF_TERM eVal = MKI(env, descP->wCtrlSz);
+
+ return esock_make_ok2(env, eVal);
+}
+
+
/* The option has *not* been encoded. Instead it has been provided
* in "native mode" (option is provided as is). In this case it will have the
* format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()}
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c
index 8bb725fb5b..ff50fd2384 100644
--- a/erts/emulator/nifs/common/socket_util.c
+++ b/erts/emulator/nifs/common/socket_util.c
@@ -1261,6 +1261,35 @@ char* esock_decode_protocol(ErlNifEnv* env,
+/* +++ esock_decode_bufsz +++
+ *
+ * Decode an buffer size. The size of a buffer is:
+ *
+ * Sz > 0 => Use provided value
+ * Sz => Use provided default
+ *
+ */
+extern
+char* esock_decode_bufsz(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ size_t defSz,
+ size_t* sz)
+{
+ int val;
+
+ if (!GET_INT(env, eVal, &val))
+ return ESOCK_STR_EINVAL;
+
+ if (val > 0)
+ *sz = (size_t) val;
+ else
+ *sz = defSz;
+
+ return NULL;
+}
+
+
+
/* *** esock_decode_string ***
*
* Decode a string value. A successful decode results in an
diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h
index a38453e238..1b5d003155 100644
--- a/erts/emulator/nifs/common/socket_util.h
+++ b/erts/emulator/nifs/common/socket_util.h
@@ -158,6 +158,12 @@ char* esock_encode_protocol(ErlNifEnv* env,
ERL_NIF_TERM* eProtocol);
extern
+char* esock_decode_bufsz(ErlNifEnv* env,
+ ERL_NIF_TERM eVal,
+ size_t defSz,
+ size_t* sz);
+
+extern
BOOLEAN_T esock_decode_string(ErlNifEnv* env,
const ERL_NIF_TERM eString,
char** stringP);
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 9e6d9f4709..a0550990e3 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 652054457f..8093bad885 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -320,12 +320,15 @@
%% Should we just document it and leave it to the user?
%% Or catch it in the encode functions?
%% A setopt for a readonly option leads to einval?
+%% Do we really need a sndbuf?
-type otp_socket_option() :: debug |
iow |
controlling_process |
rcvbuf |
- sndbuf.
+ sndbuf |
+ rcvctrlbuf |
+ sndctrlbuf.
%% Shall we have special treatment of linger??
%% read-only options:
%% domain | protocol | type.
@@ -645,6 +648,10 @@
-define(SOCKET_OPT_OTP_DEBUG, 1).
-define(SOCKET_OPT_OTP_IOW, 2).
-define(SOCKET_OPT_OTP_CTRL_PROC, 3).
+-define(SOCKET_OPT_OTP_RCVBUF, 4).
+%%-define(SOCKET_OPT_OTP_SNDBUF, 5).
+-define(SOCKET_OPT_OTP_RCVCTRLBUF, 6).
+-define(SOCKET_OPT_OTP_SNDCTRLBUF, 7).
%% *** SOCKET (socket) options
-define(SOCKET_OPT_SOCK_ACCEPTCONN, 1).
@@ -2136,9 +2143,9 @@ getopt(#socket{ref = SockRef}, Level, Key) ->
end
end
catch
- throw:T ->
- T;
- error:Reason ->
+ throw:E:_S ->
+ E;
+ error:Reason:_Stack ->
{error, Reason} % Process more?
end.
@@ -2401,6 +2408,18 @@ enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) ->
V;
enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) ->
V;
+enc_setopt_value(otp, rcvbuf, V, _, _, _) when (V =:= default) ->
+ 0;
+enc_setopt_value(otp, rcvbuf, V, _, _, _) when is_integer(V) andalso (V > 0) ->
+ V;
+enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when (V =:= default) ->
+ 0;
+enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) ->
+ V;
+enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when (V =:= default) ->
+ 0;
+enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) ->
+ V;
enc_setopt_value(otp = L, Opt, V, _D, _T, _P) ->
not_supported({L, Opt, V});
@@ -2871,6 +2890,12 @@ enc_sockopt_key(otp, iow, _, _, _, _) ->
?SOCKET_OPT_OTP_IOW;
enc_sockopt_key(otp, controlling_process, _, _, _, _) ->
?SOCKET_OPT_OTP_CTRL_PROC;
+enc_sockopt_key(otp, rcvbuf, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_RCVBUF;
+enc_sockopt_key(otp, rcvctrlbuf, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_RCVCTRLBUF;
+enc_sockopt_key(otp, sndctrlbuf, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_SNDCTRLBUF;
enc_sockopt_key(otp = L, Opt, _, _, _, _) ->
not_supported({L, Opt});
diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl
index 0255c8ef75..d4cd144168 100644
--- a/lib/kernel/test/socket_SUITE.erl
+++ b/lib/kernel/test/socket_SUITE.erl
@@ -38,6 +38,9 @@
api_b_send_and_recv_tcp4/1,
api_b_sendmsg_and_recvmsg_tcp4/1,
+ %% API Options
+ api_opt_simple_otp_options/1,
+
%% API Operation Timeout
api_to_connect_tcp4/1,
api_to_connect_tcp6/1,
@@ -90,13 +93,15 @@ all() ->
groups() ->
[{api, [], api_cases()},
{api_basic, [], api_basic_cases()},
- {api_op_with_timeout, [], api_op_with_timeout_cases()}
+ {api_op_with_timeout, [], api_op_with_timeout_cases()},
+ {api_options, [], api_options_cases()}
%% {tickets, [], ticket_cases()}
].
api_cases() ->
[
{group, api_basic},
+ {group, api_options},
{group, api_op_with_timeout}
].
@@ -110,6 +115,11 @@ api_basic_cases() ->
api_b_sendmsg_and_recvmsg_tcp4
].
+api_options_cases() ->
+ [
+ api_opt_simple_otp_options
+ ].
+
api_op_with_timeout_cases() ->
[
api_to_connect_tcp4,
@@ -433,6 +443,92 @@ api_b_send_and_recv_tcp(Domain, Send, Recv) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Perform some simple getopt and setopt with the level = otp options
+api_opt_simple_otp_options(suite) ->
+ [];
+api_opt_simple_otp_options(doc) ->
+ [];
+api_opt_simple_otp_options(_Config) when is_list(_Config) ->
+ tc_begin(api_opt_simple_otp_options),
+
+ p("Create sockets"),
+ S1 = sock_open(inet, stream, tcp),
+ S2 = sock_open(inet, dgram, udp),
+
+ Get = fun(S, Key) ->
+ socket:getopt(S, otp, Key)
+ end,
+ Set = fun(S, Key, Val) ->
+ socket:setopt(S, otp, Key, Val)
+ end,
+
+ p("Create dummy process"),
+ Pid = spawn_link(fun() ->
+ receive
+ die ->
+ exit(normal)
+ end
+ end),
+
+ F = fun(Sock) ->
+ p("Test IOW"),
+ {ok, IOW} = Get(Sock, iow),
+ NotIOW = not IOW,
+ ok = Set(Sock, iow, NotIOW),
+ {ok, NotIOW} = Get(Sock, iow),
+
+ p("Test rcvbuf"),
+ {ok, RcvBuf} = Get(Sock, rcvbuf),
+ RcvBuf2 = RcvBuf*2,
+ ok = Set(Sock, rcvbuf, RcvBuf2),
+ {ok, RcvBuf2} = Get(Sock, rcvbuf),
+ ok = Set(Sock, rcvbuf, default),
+ {ok, RcvBuf} = Get(Sock, rcvbuf),
+
+ p("Test rcvctrlbuf"),
+ {ok, RcvCtrlBuf} = Get(Sock, rcvctrlbuf),
+ RcvCtrlBuf2 = RcvCtrlBuf*2,
+ ok = Set(Sock, rcvctrlbuf, RcvCtrlBuf2),
+ {ok, RcvCtrlBuf2} = Get(Sock, rcvctrlbuf),
+ ok = Set(Sock, rcvctrlbuf, default),
+ {ok, RcvCtrlBuf} = Get(Sock, rcvctrlbuf),
+
+ p("Test sndctrlbuf"),
+ {ok, SndCtrlBuf} = Get(Sock, sndctrlbuf),
+ SndCtrlBuf2 = SndCtrlBuf*2,
+ ok = Set(Sock, sndctrlbuf, SndCtrlBuf2),
+ {ok, SndCtrlBuf2} = Get(Sock, sndctrlbuf),
+ ok = Set(Sock, sndctrlbuf, default),
+ {ok, RcvCtrlBuf} = Get(Sock, sndctrlbuf),
+
+ p("Test controlling-process"),
+ Self = self(),
+ {ok, Self} = Get(Sock, controlling_process),
+ ok = Set(Sock, controlling_process, Pid),
+ {ok, Pid} = Get(Sock, controlling_process)
+
+ end,
+
+ p("Test stream/tcp "),
+ F(S1),
+
+ p("Test dgram/udp "),
+ F(S2),
+
+ p("kill dummy process"),
+ %% This will also close its sockets (S1 and S2),
+ %% This should really be tested explicitly...
+ Pid ! die,
+
+ %% p("close sockets"),
+ %% sock_close(S1),
+ %% sock_close(S2),
+
+ tc_end().
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% This test case is intended to test the connect timeout option
%% on an IPv4 TCP (stream) socket.
api_to_connect_tcp4(suite) ->
@@ -1067,6 +1163,20 @@ sock_accept(LSock) ->
end.
+sock_close(Sock) ->
+ try socket:close(Sock) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ p("sock_close -> error: ~p", [Reason]),
+ ?FAIL({close, Reason})
+ catch
+ C:E:S ->
+ p("sock_close -> failed: ~p, ~p, ~p", [C, E, S]),
+ ?FAIL({close, C, E, S})
+ end.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%