summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErlang/OTP <otp@erlang.org>2020-09-10 14:27:08 +0200
committerErlang/OTP <otp@erlang.org>2020-09-10 14:27:08 +0200
commitb64d7a8edcebf7ac267218f51efb58663cbfa9bc (patch)
treee4c99037bc6cffbdbbe68586f5aa499b8bc5626e
parent44b6531bc575bac4eccab7eea2b27167f0d324aa (diff)
parent942530a071adf9e8352d4412ea96aa1b5a0ed79d (diff)
downloaderlang-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.c4
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl68
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.