Skip to content

Commit a582f34

Browse files
committed
kernel: Test the adaptive esock rcvbuf
Covers that a length 0 recv on a bulk stream returns chunks larger than the default buffer, that an explicitly set (otp) rcvbuf still bounds the chunks while getopt reports the configured size, and that dgram sockets keep truncating at the configured size.
1 parent fbf5e58 commit a582f34

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

lib/kernel/test/socket_api_SUITE.erl

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@
200200
api_opt_simple_otp_options/1,
201201
api_opt_simple_otp_meta_option/1,
202202
api_opt_simple_otp_rcvbuf_option/1,
203+
api_opt_adaptive_otp_rcvbuf_option/1,
203204
api_opt_simple_otp_controlling_process/1,
204205
api_opt_sock_acceptconn_udp/1,
205206
api_opt_sock_acceptconn_tcp/1,
@@ -510,6 +511,7 @@ api_options_otp_cases() ->
510511
api_opt_simple_otp_options,
511512
api_opt_simple_otp_meta_option,
512513
api_opt_simple_otp_rcvbuf_option,
514+
api_opt_adaptive_otp_rcvbuf_option,
513515
api_opt_simple_otp_controlling_process
514516
].
515517

@@ -12024,6 +12026,137 @@ api_opt_simple_otp_rcvbuf_option() ->
1202412026

1202512027

1202612028

12029+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12030+
12031+
%% The buffer used by a recv with Length = 0 on a stream socket adapts
12032+
%% to the traffic, unless the (otp) rcvbuf option has been set, in
12033+
%% which case that size is used (and bounds the returned chunks) as
12034+
%% before. Dgram sockets read into the configured size as before.
12035+
%% Adaptation is not implemented on Windows.
12036+
api_opt_adaptive_otp_rcvbuf_option(_Config) when is_list(_Config) ->
12037+
?TT(?SECS(30)),
12038+
tc_try(?FUNCTION_NAME,
12039+
fun() ->
12040+
has_support_ipv4(),
12041+
is_not_windows()
12042+
end,
12043+
fun() ->
12044+
api_opt_adaptive_otp_rcvbuf_option()
12045+
end).
12046+
12047+
api_opt_adaptive_otp_rcvbuf_option() ->
12048+
LSA = which_local_socket_addr(inet),
12049+
12050+
{ok, L} = socket:open(inet, stream, tcp),
12051+
ok = socket:bind(L, LSA#{port => 0}),
12052+
ok = socket:listen(L),
12053+
{ok, SSA} = socket:sockname(L),
12054+
{ok, Default} = socket:getopt(L, otp, rcvbuf),
12055+
12056+
i("verify the buffer adapts to bulk traffic (default rcvbuf ~w)",
12057+
[Default]),
12058+
Bulk = 64 * 1024 * 1024,
12059+
Client1 = aor_stream_client(SSA, Bulk),
12060+
{ok, S1} = socket:accept(L),
12061+
MaxChunk1 = aor_drain(S1, Bulk, 0),
12062+
i("max chunk: ~w", [MaxChunk1]),
12063+
if
12064+
MaxChunk1 > Default ->
12065+
ok;
12066+
true ->
12067+
exit({no_adaptation, MaxChunk1, Default})
12068+
end,
12069+
%% The adapted size is internal; getopt reports the configured size
12070+
{ok, Default} = socket:getopt(S1, otp, rcvbuf),
12071+
aor_stop_client(Client1),
12072+
_ = socket:close(S1),
12073+
12074+
i("verify an explicitly set rcvbuf bounds the chunks"),
12075+
Pinned = 2048,
12076+
Bulk2 = 8 * 1024 * 1024,
12077+
Client2 = aor_stream_client(SSA, Bulk2),
12078+
{ok, S2} = socket:accept(L),
12079+
ok = socket:setopt(S2, otp, rcvbuf, Pinned),
12080+
MaxChunk2 = aor_drain(S2, Bulk2, 0),
12081+
i("max chunk: ~w", [MaxChunk2]),
12082+
if
12083+
MaxChunk2 =< Pinned ->
12084+
ok;
12085+
true ->
12086+
exit({not_pinned, MaxChunk2, Pinned})
12087+
end,
12088+
aor_stop_client(Client2),
12089+
_ = socket:close(S2),
12090+
_ = socket:close(L),
12091+
12092+
i("verify a dgram socket does not adapt"),
12093+
{ok, U} = socket:open(inet, dgram, udp),
12094+
ok = socket:bind(U, LSA#{port => 0}),
12095+
{ok, USA} = socket:sockname(U),
12096+
{ok, C} = socket:open(inet, dgram, udp),
12097+
ok = socket:setopt(C, socket, sndbuf, 64 * 1024),
12098+
%% Datagrams that exactly fill the buffer would grow it if
12099+
%% adaptation was (wrongly) applied to dgram sockets
12100+
Fill = binary:copy(<<$x>>, Default),
12101+
Send = fun(Data) ->
12102+
case socket:sendto(C, Data, USA) of
12103+
ok -> ok;
12104+
{error, emsgsize} -> skip("dgram size not supported")
12105+
end
12106+
end,
12107+
[begin
12108+
ok = Send(Fill),
12109+
{ok, D} = socket:recv(U, 0, ?SECS(5)),
12110+
Default = byte_size(D)
12111+
end || _ <- lists:seq(1, 8)],
12112+
%% An oversized datagram is still truncated at the configured size
12113+
ok = Send(binary:copy(<<$y>>, Default + 4096)),
12114+
{ok, T} = socket:recv(U, 0, ?SECS(5)),
12115+
i("oversized dgram read back as ~w bytes", [byte_size(T)]),
12116+
Default = byte_size(T),
12117+
_ = socket:close(C),
12118+
_ = socket:close(U),
12119+
ok.
12120+
12121+
aor_stream_client(SSA, Bytes) ->
12122+
Self = self(),
12123+
spawn_monitor(
12124+
fun() ->
12125+
{ok, S} = socket:open(inet, stream, tcp),
12126+
ok = socket:connect(S, SSA),
12127+
Chunk = binary:copy(<<$x>>, 1024 * 1024),
12128+
aor_send(S, Chunk, Bytes),
12129+
receive
12130+
{Self, stop} ->
12131+
_ = socket:close(S),
12132+
exit(normal)
12133+
end
12134+
end).
12135+
12136+
aor_send(_S, _Chunk, Bytes) when Bytes =< 0 ->
12137+
ok;
12138+
aor_send(S, Chunk, Bytes) ->
12139+
ok = socket:send(S, Chunk),
12140+
aor_send(S, Chunk, Bytes - byte_size(Chunk)).
12141+
12142+
aor_drain(_S, Bytes, MaxChunk) when Bytes =< 0 ->
12143+
MaxChunk;
12144+
aor_drain(S, Bytes, MaxChunk) ->
12145+
{ok, Data} = socket:recv(S, 0, ?SECS(10)),
12146+
Sz = byte_size(Data),
12147+
aor_drain(S, Bytes - Sz, max(Sz, MaxChunk)).
12148+
12149+
aor_stop_client({Pid, MRef}) ->
12150+
Pid ! {self(), stop},
12151+
receive
12152+
{'DOWN', MRef, process, Pid, normal} ->
12153+
ok;
12154+
{'DOWN', MRef, process, Pid, Reason} ->
12155+
exit({client, Reason})
12156+
end.
12157+
12158+
12159+
1202712160
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202812161

1202912162
%% Perform some simple getopt and setopt with the level = otp options

0 commit comments

Comments
 (0)