Skip to content

Commit 11fbf91

Browse files
committed
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. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent 0c598ab commit 11fbf91

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)