Skip to content

Commit 081c77f

Browse files
Merge pull request erlang#9615 from juhlig/gen_server_plain_timeout_fix OTP-19537
Fix `gen_server` plain timeouts being canceled by `system` messages
2 parents f9e0787 + 468e200 commit 081c77f

File tree

2 files changed

+61
-31
lines changed

2 files changed

+61
-31
lines changed

lib/stdlib/src/gen_server.erl

+25-31
Original file line numberDiff line numberDiff line change
@@ -2256,7 +2256,7 @@ loop(ServerData, State, {continue, Continue} = Msg, Debug) ->
22562256
loop(ServerData, State, LoopAction, Debug) ->
22572257
case LoopAction of
22582258
{timeout_zero, TimeoutMsg} ->
2259-
decode_msg(ServerData, State, TimeoutMsg, Debug, [], false);
2259+
decode_msg(ServerData, State, infinity, Debug, [], TimeoutMsg);
22602260
{timeout, Timer, HibInf} ->
22612261
loop(ServerData, State, HibInf, Debug, Timer);
22622262
HibT ->
@@ -2267,46 +2267,39 @@ loop(ServerData, State, hibernate, Debug, Timer) ->
22672267
receive
22682268
Msg ->
22692269
erlang:garbage_collect(),
2270-
decode_msg(ServerData, State, Msg, Debug, Timer, true)
2270+
decode_msg(ServerData, State, hibernate, Debug, Timer, Msg)
22712271
after 0 ->
2272-
loop_hibernate(ServerData, State, Debug, Timer)
2272+
loop_hibernate(ServerData, State, Timer, Debug)
22732273
end;
22742274
%%
2275-
loop(#server_data{hibernate_after = HibernateAfterTimeout} = ServerData, State, infinity, Debug, Timer) ->
2275+
loop(ServerData, State, infinity, Debug, Timer) ->
22762276
receive
22772277
Msg ->
2278-
decode_msg(ServerData, State, Msg, Debug, Timer, false)
2279-
after HibernateAfterTimeout ->
2280-
loop_hibernate(ServerData, State, Debug, Timer)
2278+
decode_msg(ServerData, State, infinity, Debug, Timer, Msg)
2279+
after ServerData#server_data.hibernate_after ->
2280+
loop_hibernate(ServerData, State, Timer, Debug)
22812281
end;
22822282
%%
22832283
loop(ServerData, State, Time, Debug, Timer)
22842284
when ?is_rel_timeout(Time) ->
2285-
Msg = receive
2286-
Input ->
2287-
Input
2288-
after Time ->
2289-
timeout
2290-
end,
2291-
decode_msg(ServerData, State, Msg, Debug, Timer, false).
2292-
2293-
loop_hibernate(ServerData, State, Debug, Timer) ->
2285+
receive
2286+
Msg ->
2287+
decode_msg(ServerData, State, Time, Debug, Timer, Msg)
2288+
after Time ->
2289+
decode_msg(ServerData, State, infinity, Debug, Timer, timeout)
2290+
end.
2291+
2292+
loop_hibernate(ServerData, State, Timer, Debug) ->
22942293
erlang:hibernate(),
2295-
loop_wakeup(ServerData, State, Debug, Timer).
2294+
loop_wakeup(ServerData, State, Timer, Debug).
22962295

2297-
loop_wakeup(ServerData, State, Debug, Timer) ->
2296+
loop_wakeup(ServerData, State, Timer, Debug) ->
22982297
receive
22992298
Msg ->
2300-
decode_msg(update_callback_cache(ServerData), State, Msg, Debug, Timer, true)
2299+
decode_msg(
2300+
update_callback_cache(ServerData), State, hibernate, Debug, Timer, Msg)
23012301
end.
23022302

2303-
loop_continue(ServerData, State, Hib, Debug, Timer) ->
2304-
Action = case Hib of
2305-
true -> hibernate;
2306-
false -> infinity
2307-
end,
2308-
loop(ServerData, State, Action, Debug, Timer).
2309-
23102303
cancel_timer([]) ->
23112304
ok;
23122305
cancel_timer([TRef | _]) ->
@@ -2332,17 +2325,17 @@ update_callback_cache(#server_data{module = Mod} = ServerData) ->
23322325
handle_info = fun Mod:handle_info/2,
23332326
handle_continue = fun Mod:handle_continue/2}.
23342327

2335-
decode_msg(#server_data{parent = Parent, tag = Tag} = ServerData, State, Msg, Debug, Timer, Hib) ->
2328+
decode_msg(#server_data{parent = Parent, tag = Tag} = ServerData, State, HibT, Debug, Timer, Msg) ->
23362329
case Msg of
23372330
{system, From, Req} ->
23382331
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
2339-
[ServerData, State, Timer, Hib], Hib);
2332+
[ServerData, State, HibT, Timer], HibT =:= hibernate);
23402333
{'EXIT', Parent, Reason} ->
23412334
terminate(ServerData, State, Msg, undefined, Reason, ?STACKTRACE(), Debug);
23422335
{timeout, TRef, Tag} when TRef =:= hd(Timer) ->
23432336
decode_msg(ServerData, State, tl(Timer), Debug);
23442337
{timeout, _Stale, Tag} ->
2345-
loop_continue(ServerData, State, Hib, Debug, Timer);
2338+
loop(ServerData, State, HibT, Debug, Timer);
23462339
_ ->
23472340
cancel_timer(Timer),
23482341
decode_msg(ServerData, State, Msg, Debug)
@@ -2613,8 +2606,9 @@ listify(Opt) ->
26132606
%% Callback functions for system messages handling.
26142607
%%-----------------------------------------------------------------
26152608
-doc false.
2616-
system_continue(Parent, Debug, [#server_data{parent=Parent} = ServerData, State, Timer, Hib]) ->
2617-
loop_continue(update_callback_cache(ServerData), State, Hib, Debug, Timer).
2609+
system_continue(
2610+
Parent, Debug, [#server_data{parent=Parent} = ServerData, State, HibT, Timer]) ->
2611+
loop(update_callback_cache(ServerData), State, HibT, Debug, Timer).
26182612

26192613
-doc false.
26202614
-spec system_terminate(_, _, _, [_]) -> no_return().

lib/stdlib/test/gen_server_SUITE.erl

+36
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
send_request_check_reqid_collection/1,
3434
cast/1, cast_fast/1, continue/1, info/1, abcast/1,
3535
handle_event_timeout/1, handle_event_timeout_zero/1,
36+
handle_event_timeout_plain/1,
3637
multicall/1, multicall_down/1, multicall_remote/1,
3738
multicall_remote_old1/1, multicall_remote_old2/1,
3839
multicall_recv_opt_success/1,
@@ -93,6 +94,7 @@ all() ->
9394
send_request_receive_reqid_collection, send_request_wait_reqid_collection,
9495
send_request_check_reqid_collection, cast, cast_fast, info, abcast,
9596
continue, handle_event_timeout, handle_event_timeout_zero,
97+
handle_event_timeout_plain,
9698
{group, multi_call},
9799
call_remote1, call_remote2, calling_self,
98100
call_remote3, call_remote_n1, call_remote_n2,
@@ -1441,6 +1443,40 @@ handle_event_timeout_zero(Config) when is_list(Config) ->
14411443
process_flag(trap_exit, OldFl),
14421444
ok.
14431445

1446+
handle_event_timeout_plain(Config) when is_list(Config) ->
1447+
OldFl = process_flag(trap_exit, true),
1448+
1449+
{ok, Pid} =
1450+
gen_server:start(gen_server_SUITE, [], []),
1451+
pong = gen_server:call(Pid, ping),
1452+
1453+
%% a `system` message should not cancel a plain timeout
1454+
ok = gen_server:cast(Pid, {self(), delayed_cast, 500}),
1455+
sys:get_status(Pid),
1456+
receive
1457+
{Pid, delayed} ->
1458+
ok
1459+
after 1000 ->
1460+
ct:fail(delayed_cast_message_not_received)
1461+
end,
1462+
1463+
%% a request (or other message) should cancel a plain timeout
1464+
ok = gen_server:cast(Pid, {self(), delayed_cast, 500}),
1465+
pong = gen_server:call(Pid, ping),
1466+
receive
1467+
{Pid, delayed} ->
1468+
ct:fail(delayed_cast_message_received)
1469+
after 1000 ->
1470+
ok
1471+
end,
1472+
1473+
ok = gen_server:call(Pid, stop),
1474+
busy_wait_for_process(Pid, 600),
1475+
{'EXIT', {noproc, _}} = (catch gen_server:call(Pid, started_p, 1)),
1476+
1477+
process_flag(trap_exit, OldFl),
1478+
ok.
1479+
14441480
hibernate(Config) when is_list(Config) ->
14451481
OldFl = process_flag(trap_exit, true),
14461482
{ok, Pid0} =

0 commit comments

Comments
 (0)