diff options
author | Erlang/OTP <otp@erlang.org> | 2020-09-10 14:27:08 +0200 |
---|---|---|
committer | Erlang/OTP <otp@erlang.org> | 2020-09-10 14:27:08 +0200 |
commit | b64d7a8edcebf7ac267218f51efb58663cbfa9bc (patch) | |
tree | e4c99037bc6cffbdbbe68586f5aa499b8bc5626e | |
parent | 44b6531bc575bac4eccab7eea2b27167f0d324aa (diff) | |
parent | 942530a071adf9e8352d4412ea96aa1b5a0ed79d (diff) | |
download | erlang-b64d7a8edcebf7ac267218f51efb58663cbfa9bc.tar.gz |
Merge branch 'max-au/erl-check-io-desync-23/OTP-16780/PR-2701' into maint-23
* max-au/erl-check-io-desync-23/OTP-16780/PR-2701:
kernel: Fix tcp misc testcase resource cleanup
erl_check_io: do not discard ERTS_POLL_EV_IN from active_events
-rw-r--r-- | erts/emulator/sys/common/erl_check_io.c | 4 | ||||
-rw-r--r-- | lib/kernel/test/gen_tcp_misc_SUITE.erl | 68 |
2 files changed, 69 insertions, 3 deletions
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 6305590690..fe9d6b29c0 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1761,8 +1761,10 @@ erts_check_io(ErtsPollThread *psi, ErtsMonotonicTime timeout_time) reactive_events = state->active_events; - if (state->flags & ERTS_EV_FLAG_IN_SCHEDULER) + if (state->flags & ERTS_EV_FLAG_IN_SCHEDULER) { reactive_events &= ~ERTS_POLL_EV_IN; + state->active_events |= ERTS_POLL_EV_IN; + } /* Reactivate the poll op if there are still active events */ if (reactive_events) { diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 55bf50edd9..220a5b55f1 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -55,7 +55,7 @@ active_once_closed/1, send_timeout/1, send_timeout_active/1, otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1, - otp_12242/1, delay_send_error/1]). + otp_12242/1, delay_send_error/1, bidirectional_traffic/1]). %% Internal exports. -export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1, @@ -100,7 +100,7 @@ all() -> active_once_closed, send_timeout, send_timeout_active, otp_7731, wrapping_oct, zombie_sockets, otp_7816, otp_8102, otp_9389, - otp_12242, delay_send_error]. + otp_12242, delay_send_error, bidirectional_traffic]. groups() -> []. @@ -3574,6 +3574,7 @@ delay_send_error(_Config) -> [{packet, 1}, {active, false}, {delay_send, true}]), {ok, S} = gen_tcp:accept(L), %% Do a couple of sends first to see that it works + ok = gen_tcp:send(C, "hello"), ok = gen_tcp:send(C, "hello"), ok = gen_tcp:send(C, "hello"), @@ -3594,3 +3595,66 @@ delay_send_error(_Config) -> ok end, ok = gen_tcp:close(C). + +-define(ACTIVE_N, 20). + +%% 30-second test for gen_tcp in {active, N} mode, ensuring it does not get stuck. +%% Verifies that erl_check_io properly handles extra EPOLLIN signals. +bidirectional_traffic(Config) when is_list(Config) -> + Workers = erlang:system_info(schedulers_online) * 2, + Payload = crypto:strong_rand_bytes(32), + {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]), + %% get all sockets to know failing ends + {ok, Port} = inet:port(LSock), + Control = self(), + Receivers = [spawn_link(fun () -> exchange(LSock, Port, Payload, Control) end) || _ <- lists:seq(1, Workers)], + Result = + receive + {timeout, Socket, Total} -> + {fail, {timeout, Socket, Total}}; + {error, Socket, Reason} -> + {fail, {error, Socket, Reason}} + after 30000 -> + %% if it does not fail in 30 seconds, it most likely works + ok + end, + [begin unlink(Rec), exit(Rec, kill) end || Rec <- Receivers], + Result. + +exchange(LSock, Port, Payload, Control) -> + %% spin up client + _ClntRcv = spawn( + fun () -> + {ok, Client} = gen_tcp:connect("localhost", Port, [binary, {packet, 0}, {active, ?ACTIVE_N}]), + send_recv_loop(Client, Payload, Control) + end), + {ok, Socket} = gen_tcp:accept(LSock), + %% sending process + send_recv_loop(Socket, Payload, Control). + +send_recv_loop(Socket, Payload, Control) -> + %% {active, N} must be set to active > 12 to trigger the issue + %% {active, 30} seems to trigger it quite often & reliably + inet:setopts(Socket, [{active, ?ACTIVE_N}]), + _Snd = spawn_link( + fun Sender() -> + _ = gen_tcp:send(Socket, Payload), + Sender() + end), + recv(Socket, 0, Control). + +recv(Socket, Total, Control) -> + receive + {tcp, Socket, Data} -> + recv(Socket, Total + byte_size(Data), Control); + {tcp_passive, Socket} -> + inet:setopts(Socket, [{active, ?ACTIVE_N}]), + recv(Socket, Total, Control); + {tcp_closed, Socket} -> + exit(terminate); + Other-> + Control ! {error, Socket, Other} + after 2000 -> + %% no data received in 2 seconds, test failed + Control ! {timeout, Socket, Total} + end. |