Skip to content

Commit baaea8c

Browse files
committed
Support handle_continue in gen_server.erl
Adds GenServer support for handle_continue (https://www.erlang.org/doc/apps/stdlib/gen_server.html#c:handle_continue/2), tests are carbon copy from upstream test suite https://github.com/erlang/otp/blob/141120ab9de7e6069ee45280dc7f6a251f89e081/lib/stdlib/test/gen_server_SUITE.erl#L1012 Added to otp back in 2017: erlang/otp#1490 - often used in elixir land. Signed-off-by: Peter M <petermm@gmail.com>
1 parent de8923e commit baaea8c

File tree

3 files changed

+124
-6
lines changed

3 files changed

+124
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ also non string parameters (e.g. `Enum.join([1, 2], ",")`
1717
- Support for Elixir `Enum.at/3`
1818
- Add support to Elixir `Enumerable` protocol also for `Enum.all?`, `Enum.any?`, `Enum.each` and
1919
`Enum.filter`
20+
- Add support for `handle_continue` callback in `gen_server`
2021

2122
### Changed
2223

libs/estdlib/src/gen_server.erl

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,29 +68,36 @@
6868

6969
-type init_result(StateType) ::
7070
{ok, State :: StateType}
71-
| {ok, State :: StateType, timeout()}
71+
| {ok, State :: StateType, timeout() | {continue, term()}}
7272
| {stop, Reason :: any()}.
7373

74+
-type handle_continue_result(StateType) ::
75+
{noreply, NewState :: StateType}
76+
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
77+
| {stop, Reason :: term(), NewState :: StateType}.
78+
7479
-type handle_call_result(StateType) ::
7580
{reply, Reply :: any(), NewState :: StateType}
76-
| {reply, Reply :: any(), NewState :: StateType, timeout()}
81+
| {reply, Reply :: any(), NewState :: StateType, timeout() | {continue, term()}}
7782
| {noreply, NewState :: StateType}
78-
| {noreply, NewState :: StateType, timeout()}
83+
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
7984
| {stop, Reason :: any(), Reply :: any(), NewState :: StateType}
8085
| {stop, Reason :: any(), NewState :: StateType}.
8186

8287
-type handle_cast_result(StateType) ::
8388
{noreply, NewState :: StateType}
84-
| {noreply, NewState :: StateType, timeout()}
89+
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
8590
| {stop, Reason :: any(), NewState :: StateType}.
8691

8792
-type handle_info(StateType) ::
8893
{noreply, NewState :: StateType}
89-
| {noreply, NewState :: StateType, timeout()}
94+
| {noreply, NewState :: StateType, timeout() | {continue, term()}}
9095
| {stop, Reason :: any(), NewState :: StateType}.
9196

9297
-callback init(Args :: any()) ->
9398
init_result(any()).
99+
-callback handle_continue(Continue :: term(), State :: StateType) ->
100+
handle_continue_result(StateType).
94101
-callback handle_call(Request :: any(), From :: {pid(), Tag :: any()}, State :: StateType) ->
95102
handle_call_result(StateType).
96103
-callback handle_cast(Request :: any(), State :: StateType) ->
@@ -154,6 +161,16 @@ init_it(Starter, Module, Args, Options) ->
154161
},
155162
infinity
156163
};
164+
{ok, ModState, {continue, NewContinue}} ->
165+
init_ack(Starter, ok),
166+
{
167+
#state{
168+
name = proplists:get_value(name, Options),
169+
mod = Module,
170+
mod_state = ModState
171+
},
172+
{continue, NewContinue}
173+
};
157174
{ok, ModState, InitTimeout} ->
158175
init_ack(Starter, ok),
159176
{
@@ -184,6 +201,7 @@ init_it(Starter, Module, Args, Options) ->
184201
end,
185202
case StateT of
186203
undefined -> ok;
204+
{State, {continue, Continue}} -> loop(State, {continue, Continue});
187205
{State, Timeout} -> loop(State, Timeout)
188206
end.
189207

@@ -434,18 +452,32 @@ reply({Pid, Ref}, Reply) ->
434452
%%
435453

436454
%% @private
455+
loop(#state{mod = Mod, mod_state = ModState} = State, {continue, Continue}) ->
456+
case Mod:handle_continue(Continue, ModState) of
457+
{noreply, NewModState} ->
458+
loop(State#state{mod_state = NewModState}, infinity);
459+
{noreply, NewModState, {continue, NewContinue}} ->
460+
loop(State#state{mod_state = NewModState}, {continue, NewContinue});
461+
{stop, Reason, NewModState} ->
462+
do_terminate(State, Reason, NewModState)
463+
end;
437464
loop(#state{mod = Mod, mod_state = ModState} = State, Timeout) ->
438465
receive
439466
{'$call', {_Pid, _Ref} = From, Request} ->
440467
case Mod:handle_call(Request, From, ModState) of
441468
{reply, Reply, NewModState} ->
442469
ok = reply(From, Reply),
443470
loop(State#state{mod_state = NewModState}, infinity);
471+
{reply, Reply, NewModState, {continue, Continue}} ->
472+
ok = reply(From, Reply),
473+
loop(State#state{mod_state = NewModState}, {continue, Continue});
444474
{reply, Reply, NewModState, NewTimeout} ->
445475
ok = reply(From, Reply),
446476
loop(State#state{mod_state = NewModState}, NewTimeout);
447477
{noreply, NewModState} ->
448478
loop(State#state{mod_state = NewModState}, infinity);
479+
{noreply, NewModState, {continue, Continue}} ->
480+
loop(State#state{mod_state = NewModState}, {continue, Continue});
449481
{noreply, NewModState, NewTimeout} ->
450482
loop(State#state{mod_state = NewModState}, NewTimeout);
451483
{stop, Reason, Reply, NewModState} ->
@@ -460,6 +492,8 @@ loop(#state{mod = Mod, mod_state = ModState} = State, Timeout) ->
460492
case Mod:handle_cast(Request, ModState) of
461493
{noreply, NewModState} ->
462494
loop(State#state{mod_state = NewModState}, infinity);
495+
{noreply, NewModState, {continue, Continue}} ->
496+
loop(State#state{mod_state = NewModState}, {continue, Continue});
463497
{noreply, NewModState, NewTimeout} ->
464498
loop(State#state{mod_state = NewModState}, NewTimeout);
465499
{stop, Reason, NewModState} ->

tests/libs/estdlib/test_gen_server.erl

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
-module(test_gen_server).
2222

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

2626
-record(state, {
2727
num_casts = 0,
@@ -36,6 +36,7 @@ test() ->
3636
ok = test_cast(),
3737
ok = test_info(),
3838
ok = test_start_link(),
39+
ok = test_continue(),
3940
ok = test_init_exception(),
4041
ok = test_late_reply(),
4142
ok = test_concurrent_clients(),
@@ -77,6 +78,50 @@ test_start_link() ->
7778
true = erlang:process_flag(trap_exit, false),
7879
ok.
7980

81+
test_continue() ->
82+
{ok, Pid} = gen_server:start_link(?MODULE, {continue, self()}, []),
83+
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),
84+
85+
gen_server:call(Pid, {continue_reply, self()}),
86+
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),
87+
88+
gen_server:call(Pid, {continue_noreply, self()}),
89+
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),
90+
91+
gen_server:cast(Pid, {continue_noreply, self()}),
92+
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),
93+
94+
Pid ! {continue_noreply, self()},
95+
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),
96+
97+
Pid ! {continue_continue, self()},
98+
[{Pid, before_continue}, {Pid, continue}, {Pid, after_continue}] = read_replies(Pid),
99+
100+
Ref = monitor(process, Pid),
101+
Pid ! continue_stop,
102+
verify_down_reason(Ref, Pid, normal).
103+
104+
read_replies(Pid) ->
105+
receive
106+
{Pid, ack} -> read_replies()
107+
after 1000 ->
108+
error
109+
end.
110+
111+
read_replies() ->
112+
receive
113+
Msg -> [Msg | read_replies()]
114+
after 0 -> []
115+
end.
116+
117+
verify_down_reason(MRef, Server, Reason) ->
118+
receive
119+
{'DOWN', MRef, process, Server, Reason} ->
120+
ok
121+
after 5000 ->
122+
error
123+
end.
124+
80125
test_cast() ->
81126
{ok, Pid} = gen_server:start(?MODULE, [], []),
82127

@@ -353,11 +398,35 @@ test_stop_noproc() ->
353398

354399
init(throwme) ->
355400
throw(throwme);
401+
init({continue, Pid}) ->
402+
io:format("init(continue) -> ~p~n", [Pid]),
403+
self() ! {after_continue, Pid},
404+
{ok, [], {continue, {message, Pid}}};
356405
init(_) ->
357406
{ok, #state{}}.
358407

408+
handle_continue({continue, Pid}, State) ->
409+
Pid ! {self(), before_continue},
410+
self() ! {after_continue, Pid},
411+
{noreply, State, {continue, {message, Pid}}};
412+
handle_continue(stop, State) ->
413+
{stop, normal, State};
414+
handle_continue({message, Pid}, State) ->
415+
Pid ! {self(), continue},
416+
{noreply, State};
417+
handle_continue({message, Pid, From}, State) ->
418+
Pid ! {self(), continue},
419+
gen_server:reply(From, ok),
420+
{noreply, State}.
421+
359422
handle_call(ping, _From, State) ->
360423
{reply, pong, State};
424+
handle_call({continue_reply, Pid}, _From, State) ->
425+
self() ! {after_continue, Pid},
426+
{reply, ok, State, {continue, {message, Pid}}};
427+
handle_call({continue_noreply, Pid}, From, State) ->
428+
self() ! {after_continue, Pid},
429+
{noreply, State, {continue, {message, Pid, From}}};
361430
handle_call(reply_ping, From, State) ->
362431
gen_server:reply(From, pong),
363432
{noreply, State};
@@ -392,6 +461,9 @@ handle_call(crash_me, _From, State) ->
392461
handle_call(crash_in_terminate, _From, State) ->
393462
{reply, ok, State#state{crash_in_terminate = true}}.
394463

464+
handle_cast({continue_noreply, Pid}, State) ->
465+
self() ! {after_continue, Pid},
466+
{noreply, State, {continue, {message, Pid}}};
395467
handle_cast(crash, _State) ->
396468
throw(test_crash);
397469
handle_cast(ping, #state{num_casts = NumCasts} = State) ->
@@ -403,6 +475,17 @@ handle_cast({set_info_timeout, Timeout}, State) ->
403475
handle_cast(_Request, State) ->
404476
{noreply, State}.
405477

478+
handle_info({after_continue, Pid}, State) ->
479+
Pid ! {self(), after_continue},
480+
Pid ! {self(), ack},
481+
{noreply, State};
482+
handle_info(continue_stop, State) ->
483+
{noreply, State, {continue, stop}};
484+
handle_info({continue_noreply, Pid}, State) ->
485+
self() ! {after_continue, Pid},
486+
{noreply, State, {continue, {message, Pid}}};
487+
handle_info({continue_continue, Pid}, State) ->
488+
{noreply, State, {continue, {continue, Pid}}};
406489
handle_info(ping, #state{num_infos = NumInfos, info_timeout = InfoTimeout} = State) ->
407490
NewState = State#state{num_infos = NumInfos + 1},
408491
case InfoTimeout of

0 commit comments

Comments
 (0)