Skip to content

Commit 990c368

Browse files
committed
Add sys implementation for gleam/otp
Factorize some gen_server/gen_statem code into new gen module Bump erlfmt to v1.7.0 (v1.1.0 and v1.7.0 only disagree on newly introduce gen module's moduledoc attribute). Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 5bb7199 commit 990c368

File tree

9 files changed

+751
-50
lines changed

9 files changed

+751
-50
lines changed

.github/workflows/check-formatting.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@ jobs:
5656
5757
erlfmt-check:
5858
runs-on: ubuntu-24.04
59-
container: erlang:27
59+
container: erlang:28
6060
steps:
6161
- uses: actions/checkout@v4
6262

6363
- name: "Check formatting with Erlang fmt"
6464
run: |
6565
cd ..
66-
git clone --depth 1 -b v1.1.0 https://github.com/WhatsApp/erlfmt.git
66+
git clone --depth 1 -b v1.7.0 https://github.com/WhatsApp/erlfmt.git
6767
cd erlfmt
6868
rebar3 as release escriptize
6969
cd ../AtomVM

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6363
- Added support for big integers in `binary_to_term/1` and `term_to_binary/1,2`
6464
- Added `proc_lib`
6565
- Added gen_server support for timeout tuples in callback return actions introduced in OTP-28.
66+
- Added `sys`
6667

6768
### Changed
6869

libs/estdlib/src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ set(ERLANG_MODULES
3636
erts_debug
3737
ets
3838
file
39+
gen
3940
gen_event
4041
gen_server
4142
gen_statem
@@ -58,6 +59,7 @@ set(ERLANG_MODULES
5859
math
5960
net
6061
proc_lib
62+
sys
6163
file
6264
logger
6365
logger_std_h

libs/estdlib/src/gen.erl

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
%
2+
% This file is part of AtomVM.
3+
%
4+
% Copyright 2025 Paul Guyot <pguyot@kallisys.net>
5+
%
6+
% Licensed under the Apache License, Version 2.0 (the "License");
7+
% you may not use this file except in compliance with the License.
8+
% You may obtain a copy of the License at
9+
%
10+
% http://www.apache.org/licenses/LICENSE-2.0
11+
%
12+
% Unless required by applicable law or agreed to in writing, software
13+
% distributed under the License is distributed on an "AS IS" BASIS,
14+
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
% See the License for the specific language governing permissions and
16+
% limitations under the License.
17+
%
18+
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
%
20+
21+
%%-----------------------------------------------------------------------------
22+
%% This module implements common code in gen_* modules, following what
23+
%% Erlang/OTP does with gen module. However, none of the functions exported
24+
%% here are public interface.
25+
%%-----------------------------------------------------------------------------
26+
27+
-module(gen).
28+
-moduledoc false.
29+
30+
-export([
31+
call/4,
32+
cast/2,
33+
reply/2
34+
]).
35+
36+
-type server_ref() :: atom() | pid().
37+
-type from() :: {pid(), reference()}.
38+
39+
%% @private
40+
-spec call(ServerRef :: server_ref(), Label :: atom(), Request :: term(), Timeout :: timeout()) ->
41+
Reply :: term() | {error, Reason :: term()}.
42+
call(ServerRef, Label, Request, Timeout) ->
43+
MonitorRef = monitor(process, ServerRef),
44+
ok =
45+
try
46+
ServerRef ! {Label, {self(), MonitorRef}, Request},
47+
ok
48+
catch
49+
error:badarg ->
50+
% Process no longer exists, monitor will send a message
51+
ok
52+
end,
53+
receive
54+
{'DOWN', MonitorRef, process, _, {E, []} = _Reason} ->
55+
exit(E);
56+
{'DOWN', MonitorRef, process, _, {_E, _L} = Reason} ->
57+
exit(Reason);
58+
{'DOWN', MonitorRef, process, _, Atom} when is_atom(Atom) ->
59+
exit(Atom);
60+
{MonitorRef, Reply} ->
61+
demonitor(MonitorRef, [flush]),
62+
Reply
63+
after Timeout ->
64+
% If Timeout is small enough (0), the error message might be timeout
65+
% instead of noproc as there could be a race condition with the monitor.
66+
demonitor(MonitorRef, [flush]),
67+
exit(timeout)
68+
end.
69+
70+
%% @private
71+
-spec cast(ServerRef :: server_ref(), Message :: any()) -> ok.
72+
cast(ServerRef, Message) ->
73+
try
74+
ServerRef ! {'$gen_cast', Message},
75+
ok
76+
catch
77+
error:_ ->
78+
% Process does not exist, ignore error
79+
ok
80+
end.
81+
82+
%% @private
83+
-spec reply(From :: from(), Reply :: any()) -> ok.
84+
reply({Pid, Ref}, Reply) ->
85+
try
86+
Pid ! {Ref, Reply},
87+
ok
88+
catch
89+
_:_ ->
90+
ok
91+
end.

libs/estdlib/src/gen_server.erl

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
-type start_ret() :: {ok, pid()} | {error, Reason :: term()}.
7070
-type start_mon_ret() :: {ok, {Pid :: pid(), MonRef :: reference()}} | {error, Reason :: term()}.
7171
-type server_ref() :: atom() | pid().
72-
-type from() :: any().
72+
-type from() :: {pid(), reference()}.
7373

7474
-type init_result(StateType) ::
7575
{ok, State :: StateType}
@@ -434,31 +434,11 @@ call(ServerRef, Request) ->
434434
-spec call(ServerRef :: server_ref(), Request :: term(), TimeoutMs :: timeout()) ->
435435
Reply :: term() | {error, Reason :: term()}.
436436
call(ServerRef, Request, TimeoutMs) ->
437-
MonitorRef = monitor(process, ServerRef),
438-
ok =
439-
try
440-
ServerRef ! {'$gen_call', {self(), MonitorRef}, Request},
441-
ok
442-
catch
443-
error:badarg ->
444-
% Process no longer exists, monitor will send a message
445-
ok
446-
end,
447-
receive
448-
{'DOWN', MonitorRef, process, _, {E, []} = _Reason} ->
449-
erlang:exit({E, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}});
450-
{'DOWN', MonitorRef, process, _, {_E, _L} = Reason} ->
451-
erlang:exit(Reason);
452-
{'DOWN', MonitorRef, process, _, Atom} when is_atom(Atom) ->
453-
erlang:exit({Atom, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}});
454-
{MonitorRef, Reply} ->
455-
demonitor(MonitorRef, [flush]),
456-
Reply
457-
after TimeoutMs ->
458-
% If TimeoutMS is small enough (0), the error message might be timeout
459-
% instead of noproc as there could be a race condition with the monitor.
460-
demonitor(MonitorRef, [flush]),
461-
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}})
437+
try
438+
gen:call(ServerRef, '$gen_call', Request, TimeoutMs)
439+
catch
440+
exit:Reason ->
441+
exit({Reason, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}})
462442
end.
463443

464444
%%-----------------------------------------------------------------------------
@@ -473,30 +453,21 @@ call(ServerRef, Request, TimeoutMs) ->
473453
%%-----------------------------------------------------------------------------
474454
-spec cast(ServerRef :: server_ref(), Request :: term()) -> ok | {error, Reason :: term()}.
475455
cast(ServerRef, Request) ->
476-
try
477-
ServerRef ! {'$gen_cast', Request}
478-
catch
479-
error:badarg ->
480-
% Process does not exist, ignore error
481-
ok
482-
end,
483-
ok.
456+
gen:cast(ServerRef, Request).
484457

485458
%%-----------------------------------------------------------------------------
486459
%% @param From the client to whom to send the reply
487460
%% @param Reply the reply to send to the client
488-
%% @returns an arbitrary term, that should be ignored
461+
%% @returns `ok'
489462
%% @doc Send a reply to a calling client.
490463
%%
491464
%% This function will send the specified reply back to the specified
492-
%% gen_server client (e.g, via call/3). The return value of this
493-
%% function can be safely ignored.
465+
%% gen_server client (e.g, via call/3).
494466
%% @end
495467
%%-----------------------------------------------------------------------------
496468
-spec reply(From :: from(), Reply :: term()) -> term().
497-
reply({Pid, Ref}, Reply) ->
498-
Pid ! {Ref, Reply},
499-
ok.
469+
reply(From, Reply) ->
470+
gen:reply(From, Reply).
500471

501472
%%
502473
%% Internal operations
@@ -532,6 +503,8 @@ loop(Parent, State, Timeout) ->
532503
%% @private
533504
handle_msg(Msg, Parent, #state{mod = Mod, mod_state = ModState} = State) ->
534505
case Msg of
506+
{system, From, Req} ->
507+
sys:handle_system_msg(Req, From, Parent, ?MODULE, [], []);
535508
{'$gen_call', {_Pid, _Ref} = From, Request} ->
536509
case Mod:handle_call(Request, From, ModState) of
537510
{reply, Reply, NewModState} ->

libs/estdlib/src/gen_statem.erl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656

5757
-type options() :: list({atom(), term()}).
5858
-type server_ref() :: atom() | pid().
59+
-type from() :: {pid(), reference()}.
5960

6061
-type action() ::
6162
{reply, From :: pid(), Reply :: any()}
@@ -209,7 +210,12 @@ call(ServerRef, Request) ->
209210
-spec call(ServerRef :: server_ref(), Request :: term(), Timeout :: timeout()) ->
210211
Reply :: term() | {error, Reason :: term()}.
211212
call(ServerRef, Request, Timeout) ->
212-
gen_server:call(ServerRef, Request, Timeout).
213+
try
214+
gen:call(ServerRef, '$gen_call', Request, Timeout)
215+
catch
216+
exit:Reason ->
217+
exit({Reason, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}})
218+
end.
213219

214220
%%-----------------------------------------------------------------------------
215221
%% @param ServerRef a reference to the gen_statem acquired via start
@@ -223,22 +229,21 @@ call(ServerRef, Request, Timeout) ->
223229
%%-----------------------------------------------------------------------------
224230
-spec cast(ServerRef :: server_ref(), Request :: term()) -> ok | {error, Reason :: term()}.
225231
cast(ServerRef, Request) ->
226-
gen_server:cast(ServerRef, Request).
232+
gen:cast(ServerRef, Request).
227233

228234
%%-----------------------------------------------------------------------------
229235
%% @param Client the client to whom to send the reply
230236
%% @param Reply the reply to send to the client
231-
%% @returns an arbitrary term, that should be ignored
237+
%% @returns `ok'
232238
%% @doc Send a reply to a calling client.
233239
%%
234240
%% This function will send the specified reply back to the specified
235-
%% gen_statem client (e.g, via call/3). The return value of this
236-
%% function can be safely ignored.
241+
%% gen_statem client (e.g, via call/3).
237242
%% @end
238243
%%-----------------------------------------------------------------------------
239-
-spec reply(Client :: pid(), Reply :: term()) -> term().
240-
reply(Client, Reply) ->
241-
gen_server:reply(Client, Reply).
244+
-spec reply(From :: from(), Reply :: term()) -> ok.
245+
reply(From, Reply) ->
246+
gen:reply(From, Reply).
242247

243248
%%
244249
%% gen_statem callbacks

0 commit comments

Comments
 (0)