Skip to content

Commit 678fcd8

Browse files
committed
Merge pull request #1283 from bettio/gen_server-start_monitor
Add support for `gen_server:start_monitor/3,4` Similar to start_link but with `monitor` option. Code for all "start with name" functions has been refactored to avoid duplication. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 0c598ab + 11fbf91 commit 678fcd8

File tree

3 files changed

+91
-16
lines changed

3 files changed

+91
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ also non string parameters (e.g. `Enum.join([1, 2], ",")`
2323
- Add support to Elixir for `Process.send/2` `Process.send_after/3/4` and `Process.cancel_timer/1`
2424
- Add support for `handle_continue` callback in `gen_server`
2525
- Support for Elixir `List.Chars` protocol
26+
- Support for `gen_server:start_monitor/3,4`
2627

2728
### Changed
2829

libs/estdlib/src/gen_server.erl

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
-export([
4444
start/3, start/4,
4545
start_link/3, start_link/4,
46+
start_monitor/3, start_monitor/4,
4647
stop/1, stop/3,
4748
call/2, call/3,
4849
cast/2,
@@ -109,9 +110,9 @@
109110

110111
%% @private
111112
do_spawn(Module, Args, Options, SpawnOpts) ->
112-
Pid = spawn_opt(?MODULE, init_it, [self(), Module, Args, Options], SpawnOpts),
113-
case wait_ack(Pid) of
114-
ok -> {ok, Pid};
113+
PidOrMonRet = spawn_opt(?MODULE, init_it, [self(), Module, Args, Options], SpawnOpts),
114+
case wait_ack(PidOrMonRet) of
115+
ok -> {ok, PidOrMonRet};
115116
{error, Reason} -> {error, Reason}
116117
end.
117118

@@ -123,6 +124,15 @@ do_spawn(Name, Module, Args, Options, SpawnOpts) ->
123124
{error, Reason} -> {error, Reason}
124125
end.
125126

127+
%% @private
128+
spawn_if_not_registered(Name, Module, Args, Options, SpawnOpts) ->
129+
case erlang:whereis(Name) of
130+
undefined ->
131+
do_spawn(Name, Module, Args, [{name, Name} | Options], SpawnOpts);
132+
Pid ->
133+
{error, {already_started, Pid}}
134+
end.
135+
126136
init_it(Starter, Name, Module, Args, Options) ->
127137
try erlang:register(Name, self()) of
128138
true ->
@@ -209,7 +219,11 @@ init_ack(Parent, Return) ->
209219
Parent ! {ack, self(), Return},
210220
ok.
211221

212-
wait_ack(Pid) ->
222+
wait_ack(Pid) when is_pid(Pid) ->
223+
receive
224+
{ack, Pid, Return} -> Return
225+
end;
226+
wait_ack({Pid, _MonRef}) when is_pid(Pid) ->
213227
receive
214228
{ack, Pid, Return} -> Return
215229
end.
@@ -246,12 +260,7 @@ crash_report(ErrStr, Parent, E, S) ->
246260
Options :: options()
247261
) -> {ok, pid()} | {error, Reason :: term()}.
248262
start({local, Name}, Module, Args, Options) when is_atom(Name) ->
249-
case erlang:whereis(Name) of
250-
undefined ->
251-
do_spawn(Name, Module, Args, [{name, Name} | Options], []);
252-
Pid ->
253-
{error, {already_started, Pid}}
254-
end.
263+
spawn_if_not_registered(Name, Module, Args, Options, []).
255264

256265
%%-----------------------------------------------------------------------------
257266
%% @param Module the module in which the gen_server callbacks are defined
@@ -292,12 +301,7 @@ start(Module, Args, Options) ->
292301
Options :: options()
293302
) -> {ok, pid()} | {error, Reason :: term()}.
294303
start_link({local, Name}, Module, Args, Options) when is_atom(Name) ->
295-
case erlang:whereis(Name) of
296-
undefined ->
297-
do_spawn(Name, Module, Args, [{name, Name} | Options], [link]);
298-
Pid ->
299-
{error, {already_started, Pid}}
300-
end.
304+
spawn_if_not_registered(Name, Module, Args, Options, [link]).
301305

302306
%%-----------------------------------------------------------------------------
303307
%% @param Module the module in which the gen_server callbacks are defined
@@ -316,6 +320,49 @@ start_link({local, Name}, Module, Args, Options) when is_atom(Name) ->
316320
start_link(Module, Args, Options) ->
317321
do_spawn(Module, Args, Options, [link]).
318322

323+
%%-----------------------------------------------------------------------------
324+
%% @param Module the module in which the gen_server callbacks are defined
325+
%% @param Args the arguments to pass to the module's init callback
326+
%% @param Options the options used to create the gen_server
327+
%% @returns the gen_server pid and monitor reference tuple if successful;
328+
%% {error, Reason}, otherwise.
329+
%% @doc Start and monitor an un-named gen_server.
330+
%%
331+
%% This function will start a gen_server instance.
332+
%%
333+
%% <em><b>Note.</b> The Options argument is currently ignored.</em>
334+
%% @end
335+
%%-----------------------------------------------------------------------------
336+
-spec start_monitor(Module :: module(), Args :: term(), Options :: options()) ->
337+
{ok, {Pid :: pid(), MonRef :: reference()}} | {error, Reason :: term()}.
338+
start_monitor(Module, Args, Options) ->
339+
do_spawn(Module, Args, Options, [monitor]).
340+
341+
%%-----------------------------------------------------------------------------
342+
%% @param ServerName the name with which to register the gen_server
343+
%% @param Module the module in which the gen_server callbacks are defined
344+
%% @param Args the arguments to pass to the module's init callback
345+
%% @param Options the options used to create the gen_server
346+
%% @returns the gen_server pid and monitor reference tuple if successful;
347+
%% {error, Reason}, otherwise.
348+
%% @doc Start and monitor a named gen_server.
349+
%%
350+
%% This function will start a gen_server instance and register the
351+
%% newly created process with the process registry. Subsequent calls
352+
%% may use the gen_server name, in lieu of the process id.
353+
%%
354+
%% <em><b>Note.</b> The Options argument is currently ignored.</em>
355+
%% @end
356+
%%-----------------------------------------------------------------------------
357+
-spec start_monitor(
358+
ServerName :: {local, Name :: atom()},
359+
Module :: module(),
360+
Args :: term(),
361+
Options :: options()
362+
) -> {ok, {Pid :: pid(), MonRef :: reference()}} | {error, Reason :: term()}.
363+
start_monitor({local, Name}, Module, Args, Options) when is_atom(Name) ->
364+
spawn_if_not_registered(Name, Module, Args, Options, [monitor]).
365+
319366
%%-----------------------------------------------------------------------------
320367
%% @equiv stop(ServerRef, normal, infinity)
321368
%% @doc Stop a previously started gen_server instance.

tests/libs/estdlib/test_gen_server.erl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ test() ->
3636
ok = test_cast(),
3737
ok = test_info(),
3838
ok = test_start_link(),
39+
ok = test_start_monitor(),
3940
ok = test_continue(),
4041
ok = test_init_exception(),
4142
ok = test_late_reply(),
@@ -78,6 +79,24 @@ test_start_link() ->
7879
true = erlang:process_flag(trap_exit, false),
7980
ok.
8081

82+
test_start_monitor() ->
83+
case get_otp_version() of
84+
Version when Version =:= atomvm orelse (is_integer(Version) andalso Version >= 23) ->
85+
{ok, {Pid, Ref}} = gen_server:start_monitor(?MODULE, [], []),
86+
87+
pong = gen_server:call(Pid, ping),
88+
pong = gen_server:call(Pid, reply_ping),
89+
ok = gen_server:cast(Pid, crash),
90+
ok =
91+
receive
92+
{'DOWN', Ref, process, Pid, _Reason} -> ok
93+
after 30000 -> timeout
94+
end,
95+
ok;
96+
_ ->
97+
ok
98+
end.
99+
81100
test_continue() ->
82101
{ok, Pid} = gen_server:start_link(?MODULE, {continue, self()}, []),
83102
[{Pid, continue}, {Pid, after_continue}] = read_replies(Pid),
@@ -392,6 +411,14 @@ test_stop_noproc() ->
392411
ok
393412
end.
394413

414+
get_otp_version() ->
415+
case erlang:system_info(machine) of
416+
"BEAM" ->
417+
list_to_integer(erlang:system_info(otp_release));
418+
_ ->
419+
atomvm
420+
end.
421+
395422
%%
396423
%% callbacks
397424
%%

0 commit comments

Comments
 (0)