|
200 | 200 | api_opt_simple_otp_options/1, |
201 | 201 | api_opt_simple_otp_meta_option/1, |
202 | 202 | api_opt_simple_otp_rcvbuf_option/1, |
| 203 | + api_opt_adaptive_otp_rcvbuf_option/1, |
203 | 204 | api_opt_simple_otp_controlling_process/1, |
204 | 205 | api_opt_sock_acceptconn_udp/1, |
205 | 206 | api_opt_sock_acceptconn_tcp/1, |
@@ -510,6 +511,7 @@ api_options_otp_cases() -> |
510 | 511 | api_opt_simple_otp_options, |
511 | 512 | api_opt_simple_otp_meta_option, |
512 | 513 | api_opt_simple_otp_rcvbuf_option, |
| 514 | + api_opt_adaptive_otp_rcvbuf_option, |
513 | 515 | api_opt_simple_otp_controlling_process |
514 | 516 | ]. |
515 | 517 |
|
@@ -12024,6 +12026,137 @@ api_opt_simple_otp_rcvbuf_option() -> |
12024 | 12026 |
|
12025 | 12027 |
|
12026 | 12028 |
|
| 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 | + |
12027 | 12160 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
12028 | 12161 |
|
12029 | 12162 | %% Perform some simple getopt and setopt with the level = otp options |
|
0 commit comments