Skip to content

Commit b84e744

Browse files
committed
Add proc_lib:spawn/1,3 and proc_lib:spawn_link/1,3
These are used by gleam/erlang Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 21ad6df commit b84e744

File tree

2 files changed

+136
-9
lines changed

2 files changed

+136
-9
lines changed

libs/estdlib/src/proc_lib.erl

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,26 @@
1919
%
2020

2121
%%-----------------------------------------------------------------------------
22-
%% @doc An implementation of the Erlang/OTP gen_server interface.
22+
%% @doc An implementation of the Erlang/OTP proc_lib interface.
2323
%%
2424
%% This module implements a strict subset of the Erlang/OTP proc_lib
2525
%% interface.
26+
%%
27+
%% When spawning a single function, calling `spawn/1' or `spawn_link/1',
28+
%% Erlang/OTP proc_lib sets `initial_call' to a tuple with a unique
29+
%% atom determining where the function was created, for example
30+
%% ``{some_module, '-work/3-fun-0-', 0}''. AtomVM literaly calls `erlang:apply/2'
31+
%% and therefore in these cases, `initial_call` is `{erlang, apply, 2}'.
2632
%%-----------------------------------------------------------------------------
2733

2834
-module(proc_lib).
2935

3036
%% Public API
3137
-export([
38+
spawn/1,
39+
spawn/3,
40+
spawn_link/1,
41+
spawn_link/3,
3242
start/3,
3343
start/4,
3444
start/5,
@@ -50,6 +60,8 @@
5060
start_spawn_option/0
5161
]).
5262

63+
-compile({no_auto_import, [spawn/3, spawn_link/3]}).
64+
5365
-include_lib("kernel/include/logger.hrl").
5466

5567
%% @doc Restricted set of spawn options. `monitor' is not supported.
@@ -59,7 +71,43 @@
5971
| {atomvm_heap_growth, erlang:atomvm_heap_growth_strategy()}
6072
| link.
6173

62-
%% @equiv start_link(Module, Function, Args, infinity)
74+
%% @equiv spawn(erlang, apply, [Fun, []])
75+
-spec spawn(fun(() -> any())) -> pid().
76+
spawn(Fun) ->
77+
spawn(erlang, apply, [Fun, []]).
78+
79+
%%-----------------------------------------------------------------------------
80+
%% @param Module of the function to call
81+
%% @param Function to call
82+
%% @param Args arguments to pass to the function
83+
%% @doc Spawn a new process and initialize it.
84+
%% @end
85+
%%-----------------------------------------------------------------------------
86+
-spec spawn(module(), atom(), [any()]) -> pid().
87+
spawn(Module, Function, Args) ->
88+
Parent = self(),
89+
Ancestors = get_ancestors(),
90+
erlang:spawn(?MODULE, init_p, [Parent, Ancestors, Module, Function, Args]).
91+
92+
%% @equiv spawn_link(erlang, apply, [Fun, []])
93+
-spec spawn_link(fun(() -> any())) -> pid().
94+
spawn_link(Fun) ->
95+
spawn_link(erlang, apply, [Fun, []]).
96+
97+
%%-----------------------------------------------------------------------------
98+
%% @param Module of the function to call
99+
%% @param Function to call
100+
%% @param Args arguments to pass to the function
101+
%% @doc Spawn and atomically link a new process and initialize it.
102+
%% @end
103+
%%-----------------------------------------------------------------------------
104+
-spec spawn_link(module(), atom(), [any()]) -> pid().
105+
spawn_link(Module, Function, Args) ->
106+
Parent = self(),
107+
Ancestors = get_ancestors(),
108+
erlang:spawn_link(?MODULE, init_p, [Parent, Ancestors, Module, Function, Args]).
109+
110+
%% @equiv start(Module, Function, Args, infinity)
63111
-spec start(module(), atom(), [any()]) -> any().
64112
start(Module, Function, Args) ->
65113
start(Module, Function, Args, infinity).
@@ -114,11 +162,7 @@ start0(Module, Function, Args, Timeout, SpawnOpts, Link) ->
114162
false -> ok
115163
end,
116164
Parent = self(),
117-
Ancestors =
118-
case get('$ancestors') of
119-
A when is_list(A) -> A;
120-
_ -> []
121-
end,
165+
Ancestors = get_ancestors(),
122166
{Pid, Monitor} = spawn_opt(?MODULE, init_p, [Parent, Ancestors, Module, Function, Args], [
123167
monitor | SpawnOpts
124168
]),
@@ -155,6 +199,13 @@ start0(Module, Function, Args, Timeout, SpawnOpts, Link) ->
155199
{error, timeout}
156200
end.
157201

202+
%% @private
203+
get_ancestors() ->
204+
case get('$ancestors') of
205+
A when is_list(A) -> A;
206+
_ -> []
207+
end.
208+
158209
%%-----------------------------------------------------------------------------
159210
%% @param Result result sent back to parent
160211
%% @doc Callback to signal that initialization succeeded.
@@ -237,7 +288,7 @@ crash_report(Class, Reason, MFA, Stacktrace) ->
237288
ICA,
238289
self(),
239290
{Class, Reason},
240-
get('$ancestors'),
291+
get_ancestors(),
241292
MessageQueueLen,
242293
Links,
243294
TotalHeapSize,

tests/libs/estdlib/test_proc_lib.erl

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

2323
-export([test/0]).
24-
-export([init_ok/1, init_crash/1, init_initial_call_ancestors/1]).
24+
-export([init_ok/1, init_crash/1, init_initial_call_ancestors/1, spawn_func/1]).
2525

2626
test() ->
2727
ok = test_start_sync(),
@@ -31,6 +31,7 @@ test() ->
3131
ok = test_start_timeout(),
3232
ok = test_start_crash(),
3333
ok = test_initial_call_and_ancestors(),
34+
ok = test_spawn(),
3435
ok.
3536

3637
test_start_sync() ->
@@ -147,6 +148,81 @@ test_initial_call_and_ancestors() ->
147148
Ret = proc_lib:start(?MODULE, init_initial_call_ancestors, [Parent]),
148149
{ok, {?MODULE, init_initial_call_ancestors, 1}, [Parent, ImaginaryAncestor]} = Ret,
149150
exit(ImaginaryAncestor, normal),
151+
erase('$ancestors'),
152+
ok.
153+
154+
spawn_func(Parent) ->
155+
{links, Links} = process_info(self(), links),
156+
Parent ! {self(), ancestors, get('$ancestors')},
157+
Parent ! {self(), initial_call, get('$initial_call')},
158+
Parent ! {self(), links, Links}.
159+
160+
test_spawn() ->
161+
Parent = self(),
162+
SpawnPid1 = proc_lib:spawn(fun() -> spawn_func(Parent) end),
163+
[Parent] =
164+
receive
165+
{SpawnPid1, ancestors, Ancestors1} -> Ancestors1
166+
after 1000 -> timeout
167+
end,
168+
{FunInfoModule, _FunInfoFunction1, FunInfoArity} =
169+
receive
170+
{SpawnPid1, initial_call, InitialCall1} -> InitialCall1
171+
after 1000 -> timeout
172+
end,
173+
[] =
174+
receive
175+
{SpawnPid1, links, Links1} -> Links1
176+
after 1000 -> timeout
177+
end,
178+
SpawnPid2 = proc_lib:spawn(?MODULE, spawn_func, [Parent]),
179+
[Parent] =
180+
receive
181+
{SpawnPid2, ancestors, Ancestors2} -> Ancestors2
182+
after 1000 -> timeout
183+
end,
184+
{test_proc_lib, spawn_func, 1} =
185+
receive
186+
{SpawnPid2, initial_call, InitialCall2} -> InitialCall2
187+
after 1000 -> timeout
188+
end,
189+
[] =
190+
receive
191+
{SpawnPid2, links, Links2} -> Links2
192+
after 1000 -> timeout
193+
end,
194+
SpawnPid3 = proc_lib:spawn_link(fun() -> spawn_func(Parent) end),
195+
[Parent] =
196+
receive
197+
{SpawnPid3, ancestors, Ancestors3} -> Ancestors3
198+
after 1000 -> timeout
199+
end,
200+
{FunInfoModule, _FunInfoFunction2, FunInfoArity} =
201+
receive
202+
{SpawnPid3, initial_call, InitialCall3} -> InitialCall3
203+
after 1000 -> timeout
204+
end,
205+
[Parent] =
206+
receive
207+
{SpawnPid3, links, Links3} -> Links3
208+
after 1000 -> timeout
209+
end,
210+
SpawnPid4 = proc_lib:spawn_link(?MODULE, spawn_func, [Parent]),
211+
[Parent] =
212+
receive
213+
{SpawnPid4, ancestors, Ancestors4} -> Ancestors4
214+
after 1000 -> timeout
215+
end,
216+
{test_proc_lib, spawn_func, 1} =
217+
receive
218+
{SpawnPid4, initial_call, InitialCall4} -> InitialCall4
219+
after 1000 -> timeout
220+
end,
221+
[Parent] =
222+
receive
223+
{SpawnPid4, links, Links4} -> Links4
224+
after 1000 -> timeout
225+
end,
150226
ok.
151227

152228
init_ok(Parent) ->

0 commit comments

Comments
 (0)