Skip to content

Commit

Permalink
Support handle_continue in gen_server.erl
Browse files Browse the repository at this point in the history
Adds GenServer support for handle_continue, tests are carbon copy from upstream test suite https://github.com/erlang/otp/blob/141120ab9de7e6069ee45280dc7f6a251f89e081/lib/stdlib/test/gen_server_SUITE.erl#L1012

Signed-off-by: Peter M <petermm@gmail.com>
  • Loading branch information
petermm committed Aug 20, 2024
1 parent db19c2c commit 75379c0
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 6 deletions.
44 changes: 39 additions & 5 deletions libs/estdlib/src/gen_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,36 @@

-type init_result(StateType) ::
{ok, State :: StateType}
| {ok, State :: StateType, timeout()}
| {ok, State :: StateType, timeout() | {continue, term()}}
| {stop, Reason :: any()}.

-type handle_continue_result(StateType) ::
{noreply, NewState :: StateType}
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
| {stop, Reason :: term(), NewState :: StateType}.

-type handle_call_result(StateType) ::
{reply, Reply :: any(), NewState :: StateType}
| {reply, Reply :: any(), NewState :: StateType, timeout()}
| {reply, Reply :: any(), NewState :: StateType, timeout() | {continue, term()}}
| {noreply, NewState :: StateType}
| {noreply, NewState :: StateType, timeout()}
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
| {stop, Reason :: any(), Reply :: any(), NewState :: StateType}
| {stop, Reason :: any(), NewState :: StateType}.

-type handle_cast_result(StateType) ::
{noreply, NewState :: StateType}
| {noreply, NewState :: StateType, timeout()}
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
| {stop, Reason :: any(), NewState :: StateType}.

-type handle_info(StateType) ::
{noreply, NewState :: StateType}
| {noreply, NewState :: StateType, timeout()}
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
| {stop, Reason :: any(), NewState :: StateType}.

-callback init(Args :: any()) ->
init_result(any()).
-callback handle_continue(Continue :: term(), State :: StateType) ->
handle_continue_result(StateType).
-callback handle_call(Request :: any(), From :: {pid(), Tag :: any()}, State :: StateType) ->
handle_call_result(StateType).
-callback handle_cast(Request :: any(), State :: StateType) ->
Expand Down Expand Up @@ -154,6 +161,16 @@ init_it(Starter, Module, Args, Options) ->
},
infinity
};
{ok, ModState, {continue, NewContinue}} ->
init_ack(Starter, ok),
{
#state{
name = proplists:get_value(name, Options),
mod = Module,
mod_state = ModState
},
{continue, NewContinue}
};
{ok, ModState, InitTimeout} ->
init_ack(Starter, ok),
{
Expand Down Expand Up @@ -184,6 +201,7 @@ init_it(Starter, Module, Args, Options) ->
end,
case StateT of
undefined -> ok;
{State, {continue, Continue}} -> loop(State, {continue, Continue});
{State, Timeout} -> loop(State, Timeout)
end.

Expand Down Expand Up @@ -434,18 +452,32 @@ reply({Pid, Ref}, Reply) ->
%%

%% @private
loop(#state{mod = Mod, mod_state = ModState} = State, {continue, Continue}) ->
case Mod:handle_continue(Continue, ModState) of
{noreply, NewModState} ->
loop(State#state{mod_state = NewModState}, infinity);
{noreply, NewModState, {continue, NewContinue}} ->
loop(State#state{mod_state = NewModState}, {continue, NewContinue});
{stop, Reason, NewModState} ->
do_terminate(State, Reason, NewModState)
end;
loop(#state{mod = Mod, mod_state = ModState} = State, Timeout) ->
receive
{'$call', {_Pid, _Ref} = From, Request} ->
case Mod:handle_call(Request, From, ModState) of
{reply, Reply, NewModState} ->
ok = reply(From, Reply),
loop(State#state{mod_state = NewModState}, infinity);
{reply, Reply, NewModState, {continue, Continue}} ->
ok = reply(From, Reply),
loop(State#state{mod_state = NewModState}, {continue, Continue});
{reply, Reply, NewModState, NewTimeout} ->
ok = reply(From, Reply),
loop(State#state{mod_state = NewModState}, NewTimeout);
{noreply, NewModState} ->
loop(State#state{mod_state = NewModState}, infinity);
{noreply, NewModState, {continue, Continue}} ->
loop(State#state{mod_state = NewModState}, {continue, Continue});
{noreply, NewModState, NewTimeout} ->
loop(State#state{mod_state = NewModState}, NewTimeout);
{stop, Reason, Reply, NewModState} ->
Expand All @@ -460,6 +492,8 @@ loop(#state{mod = Mod, mod_state = ModState} = State, Timeout) ->
case Mod:handle_cast(Request, ModState) of
{noreply, NewModState} ->
loop(State#state{mod_state = NewModState}, infinity);
{noreply, NewModState, {continue, Continue}} ->
loop(State#state{mod_state = NewModState}, {continue, Continue});
{noreply, NewModState, NewTimeout} ->
loop(State#state{mod_state = NewModState}, NewTimeout);
{stop, Reason, NewModState} ->
Expand Down
85 changes: 84 additions & 1 deletion tests/libs/estdlib/test_gen_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
-module(test_gen_server).

-export([test/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
-export([init/1, handle_continue/2, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).

-record(state, {
num_casts = 0,
Expand All @@ -36,6 +36,7 @@ test() ->
ok = test_cast(),
ok = test_info(),
ok = test_start_link(),
ok = test_continue(),
ok = test_init_exception(),
ok = test_late_reply(),
ok = test_concurrent_clients(),
Expand Down Expand Up @@ -77,6 +78,50 @@ test_start_link() ->
true = erlang:process_flag(trap_exit, false),
ok.

test_continue() ->
{ok, Pid} = gen_server:start_link(?MODULE, {continue, self()}, []),
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),

gen_server:call(Pid, {continue_reply, self()}),
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),

gen_server:call(Pid, {continue_noreply, self()}),
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),

gen_server:cast(Pid, {continue_noreply, self()}),
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),

Pid ! {continue_noreply, self()},
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),

Pid ! {continue_continue, self()},
[{Pid, before_continue}, {Pid, continue}, {Pid, after_continue}] = read_replies(Pid),

Ref = monitor(process, Pid),
Pid ! continue_stop,
verify_down_reason(Ref, Pid, normal).

read_replies(Pid) ->
receive
{Pid, ack} -> read_replies()
after 1000 ->
error
end.

read_replies() ->
receive
Msg -> [Msg | read_replies()]
after 0 -> []
end.

verify_down_reason(MRef, Server, Reason) ->
receive
{'DOWN', MRef, process, Server, Reason} ->
ok
after 5000 ->
error
end.

test_cast() ->
{ok, Pid} = gen_server:start(?MODULE, [], []),

Expand Down Expand Up @@ -353,11 +398,35 @@ test_stop_noproc() ->

init(throwme) ->
throw(throwme);
init({continue, Pid}) ->
io:format("init(continue) -> ~p~n", [Pid]),
self() ! {after_continue, Pid},
{ok, [], {continue, {message, Pid}}};
init(_) ->
{ok, #state{}}.

handle_continue({continue, Pid}, State) ->
Pid ! {self(), before_continue},
self() ! {after_continue, Pid},
{noreply, State, {continue, {message, Pid}}};
handle_continue(stop, State) ->
{stop, normal, State};
handle_continue({message, Pid}, State) ->
Pid ! {self(), continue},
{noreply, State};
handle_continue({message, Pid, From}, State) ->
Pid ! {self(), continue},
gen_server:reply(From, ok),
{noreply, State}.

handle_call(ping, _From, State) ->
{reply, pong, State};
handle_call({continue_reply, Pid}, _From, State) ->
self() ! {after_continue, Pid},
{reply, ok, State, {continue, {message, Pid}}};
handle_call({continue_noreply, Pid}, From, State) ->
self() ! {after_continue, Pid},
{noreply, State, {continue, {message, Pid, From}}};
handle_call(reply_ping, From, State) ->
gen_server:reply(From, pong),
{noreply, State};
Expand Down Expand Up @@ -392,6 +461,9 @@ handle_call(crash_me, _From, State) ->
handle_call(crash_in_terminate, _From, State) ->
{reply, ok, State#state{crash_in_terminate = true}}.

handle_cast({continue_noreply, Pid}, State) ->
self() ! {after_continue, Pid},
{noreply, State, {continue, {message, Pid}}};
handle_cast(crash, _State) ->
throw(test_crash);
handle_cast(ping, #state{num_casts = NumCasts} = State) ->
Expand All @@ -403,6 +475,17 @@ handle_cast({set_info_timeout, Timeout}, State) ->
handle_cast(_Request, State) ->
{noreply, State}.

handle_info({after_continue, Pid}, State) ->
Pid ! {self(), after_continue},
Pid ! {self(), ack},
{noreply, State};
handle_info(continue_stop, State) ->
{noreply, State, {continue, stop}};
handle_info({continue_noreply, Pid}, State) ->
self() ! {after_continue, Pid},
{noreply, State, {continue, {message, Pid}}};
handle_info({continue_continue, Pid}, State) ->
{noreply, State, {continue, {continue, Pid}}};
handle_info(ping, #state{num_infos = NumInfos, info_timeout = InfoTimeout} = State) ->
NewState = State#state{num_infos = NumInfos + 1},
case InfoTimeout of
Expand Down

0 comments on commit 75379c0

Please sign in to comment.