Skip to content

Commit

Permalink
Implement more functions
Browse files Browse the repository at this point in the history
Implement the following functions:
- `erlang:spawn_link/1`
- `erlang:spawn_link/3`
- `erlang:exit/2`
- `lists:usort/1`
- `lists:usort/2`

Fix options of `spawn_opt/2` by factorizing it with `spawn_opt/4`
Also rewrite `spawn/1` and `spawn/3` in Erlang and update tests accordingly
Implement info `links` for `erlang:process_info/2`
Also narrow type specification of `erlang:process_info/2`
Also add documentation for `erlang:exit/1`
Fix a bug where exit reason was incorrectly a tuple for exception of class exit
No longer dump crash logs when exit reason is normal
Also update `.gitignore`
Also isolate libs tests
Also remove useless second installation of Erlang/OTP in documentation workflow
Also bump OTP and Elixir versions in documentation workflow
Also update edown with a patch to fix crash when parsing updated doc for
`erlang:process_info/2`
See:
- uwiger/edown#23
- erlang/otp#7576

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Aug 19, 2023
1 parent a16b543 commit a84426f
Show file tree
Hide file tree
Showing 79 changed files with 1,166 additions and 345 deletions.
9 changes: 2 additions & 7 deletions .github/workflows/publish-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ jobs:
- uses: erlef/setup-beam@v1
with:
otp-version: "24"
elixir-version: "1.14"
otp-version: "25"
elixir-version: "1.15"

- name: Install rebar3
working-directory: /tmp
Expand All @@ -70,11 +70,6 @@ jobs:
./rebar3 local install
echo "/home/runner/.cache/rebar3/bin" >> ${GITHUB_PATH}
- uses: erlef/setup-beam@v1
with:
otp-version: "24"
elixir-version: "1.14"

- uses: actions/checkout@v3
with:
repository: ${{ vars.GITHUB_REPOSITORY }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ src/platforms/esp32/sdkconfig
!src/platforms/esp32/components/libatomvm/**
.idea/**
.vscode/**
.DS_Store
.cache
.clang-tidy
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ functions that default to `?ATOMVM_NVS_NS` are deprecated now).
- Added most format possibilities to `io:format/2` and `io_lib:format/2`
- Added `unicode` module with `characters_to_list/1,2` and `characters_to_binary/1,2,3` functions
- Added support for `crypto:hash/2` (ESP32 and generic_unix with openssl)
- Added erlang:spawn_link/1,3
- Added erlang:exit/2
- Added lists:usort/1,2
- Added links to process_info/2
- Added many missing documentation and specifications for available nifs

### Fixed
- Fixed issue with formatting integers with io:format() on STM32 platform
Expand All @@ -75,6 +80,8 @@ functions that default to `?ATOMVM_NVS_NS` are deprecated now).
- Fixed SNTP support that had been broken in IDF 4.x builds
- Fixed `erlang:send/2` not sending to registered name
- Fixed memory allocation with put-classified opcodes
- Fixed incorrect exit reason for exceptions of class exit
- Fixed several incorrect type specifications

### Breaking Changes

Expand Down
2 changes: 1 addition & 1 deletion doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ endforeach(SOURCE_TARGET)

# Support for edoc -> markdown.
add_custom_target(edown-escript
COMMAND rebar3 get-deps co edown edoc && rebar3 escriptize edown edoc
COMMAND rebar3 compile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/edoc/edown_dep
COMMENT "Preparing edown escript" VERBATIM
)
Expand Down
2 changes: 1 addition & 1 deletion doc/edoc/edown_dep/rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

{erl_opts, [debug_info]}.
{deps, [edown]}.
{deps, [{edown, {git, "https://github.com/pguyot/edown.git", {ref, "e0201c8ec6444d8d41891f0a5ed2bce42fe944d0"}}}]}.
{plugins, [
ex_doc
]}.
92 changes: 86 additions & 6 deletions libs/estdlib/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
whereis/1,
spawn/1,
spawn/3,
spawn_link/1,
spawn_link/3,
spawn_opt/2,
spawn_opt/4,
link/1,
Expand All @@ -83,6 +85,8 @@
monitor/2,
demonitor/1,
demonitor/2,
exit/1,
exit/2,
open_port/2,
system_time/1,
group_leader/0,
Expand Down Expand Up @@ -193,12 +197,18 @@ send_after(Time, Dest, Msg) ->
%% <li><b>stack_size</b> the number of words used in the stack (integer)</li>
%% <li><b>message_queue_len</b> the number of messages enqueued for the process (integer)</li>
%% <li><b>memory</b> the estimated total number of bytes in use by the process (integer)</li>
%% <li><b>links</b> the list of linked processes</li>
%% </ul>
%% Specifying an unsupported term or atom raises a bad_arg error.
%%
%% @end
%%-----------------------------------------------------------------------------
-spec process_info(Pid :: pid(), Key :: atom()) -> term().
-spec process_info
(Pid :: pid(), heap_size) -> {heap_size, non_neg_integer()};
(Pid :: pid(), stack_size) -> {stack_size, non_neg_integer()};
(Pid :: pid(), message_queue_len) -> {message_queue_len, non_neg_integer()};
(Pid :: pid(), memory) -> {memory, non_neg_integer()};
(Pid :: pid(), links) -> {links, [pid()]}.
process_info(_Pid, _Key) ->
erlang:nif_error(undefined).

Expand Down Expand Up @@ -768,8 +778,8 @@ whereis(_Name) ->
%% @end
%%-----------------------------------------------------------------------------
-spec spawn(Function :: function()) -> pid().
spawn(_Name) ->
erlang:nif_error(undefined).
spawn(Function) ->
erlang:spawn_opt(Function, []).

%%-----------------------------------------------------------------------------
%% @param Module module of the function to create a process from
Expand All @@ -780,8 +790,31 @@ spawn(_Name) ->
%% @end
%%-----------------------------------------------------------------------------
-spec spawn(Module :: module(), Function :: atom(), Args :: [any()]) -> pid().
spawn(_Module, _Function, _Args) ->
erlang:nif_error(undefined).
spawn(Module, Function, Args) ->
erlang:spawn_opt(Module, Function, Args, []).

%%-----------------------------------------------------------------------------
%% @param Function function to create a process from
%% @returns pid of the new process
%% @doc Create a new process and link it.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_link(Function :: function()) -> pid().
spawn_link(Function) ->
erlang:spawn_opt(Function, [link]).

%%-----------------------------------------------------------------------------
%% @param Module module of the function to create a process from
%% @param Function name of the function to create a process from
%% @param Args arguments to pass to the function to create a process from
%% @returns pid of the new process
%% @doc Create a new process by calling exported Function from Module with Args
%% and link it.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_link(Module :: module(), Function :: atom(), Args :: [any()]) -> pid().
spawn_link(Module, Function, Args) ->
erlang:spawn_opt(Module, Function, Args, [link]).

-type spawn_option() ::
{min_heap_size, pos_integer()}
Expand All @@ -796,7 +829,7 @@ spawn(_Module, _Function, _Args) ->
%% @doc Create a new process.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_opt(Function :: function(), Options :: [{max_heap_size, integer()}]) ->
-spec spawn_opt(Function :: function(), Options :: [spawn_option()]) ->
pid() | {pid(), reference()}.
spawn_opt(_Name, _Options) ->
erlang:nif_error(undefined).
Expand Down Expand Up @@ -898,6 +931,53 @@ demonitor(_Monitor) ->
demonitor(_Monitor, _Options) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Reason reason for exit
%% @doc Raises an exception of class `exit' with reason `Reason'.
%% The exception can be caught. If it is not, the process exits.
%% If the exception is not caught the signal is sent to linked processes.
%% In this case, if `Reason' is `kill', it is not transformed into `killed' and
%% linked processes can trap it (unlike `exit/2').
%% @end
%%-----------------------------------------------------------------------------
-spec exit(Reason :: any()) -> no_return().
exit(_Reason) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Process target process
%% @param Reason reason for exit
%% @returns `true'
%% @doc Send an exit signal to target process.
%% The consequences of the exit signal depends on `Reason', on whether
%% `Process' is self() or another process and whether target process is
%% trapping exit.
%% If `Reason' is not `kill' nor `normal':
%% <ul>
%% <li>If target process is not trapping exits, it exits with `Reason'</li>
%% <li>If traget process is trapping exits, it receives a message
%% ``{'EXIT', From, Reason}'' where `From' is the caller of `exit/2'.</li>
%% </ul>
%% If `Reason' is `kill', the target process exits with `Reason' changed to
%% `killed'.
%% If `Reason' is `normal' and `Process' is not `self()':
%% <ul>
%% <li>If target process is not trapping exits, nothing happens.</li>
%% <li>If traget process is trapping exits, it receives a message
%% ``{'EXIT', From, normal}'' where `From' is the caller of `exit/2'.</li>
%% </ul>
%% If `Reason' is `normal' and `Process' is `self()':
%% <ul>
%% <li>If target process is not trapping exits, it exits with `normal'.</li>
%% <li>If traget process is trapping exits, it receives a message
%% ``{'EXIT', From, normal}'' where `From' is the caller of `exit/2'.</li>
%% </ul>
%% @end
%%-----------------------------------------------------------------------------
-spec exit(Process :: pid(), Reason :: any()) -> true.
exit(_Process, _Reason) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param PortName Tuple {spawn, Name} identifying the port
%% @param Options Options, meaningful for the port
Expand Down
49 changes: 49 additions & 0 deletions libs/estdlib/src/lists.erl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
join/2,
seq/2, seq/3,
sort/1, sort/2,
usort/1, usort/2,
duplicate/2,
sublist/2
]).
Expand Down Expand Up @@ -464,6 +465,7 @@ seq(From, To, Incr, Accum) ->
%%
%% @end
%%-----------------------------------------------------------------------------
-spec sort(List :: [T]) -> [T].
sort(List) when is_list(List) ->
sort(fun lt/2, List).

Expand All @@ -475,6 +477,7 @@ sort(List) when is_list(List) ->
%%
%% @end
%%-----------------------------------------------------------------------------
-spec sort(Fun :: fun((T, T) -> boolean()), List :: [T]) -> [T].
sort(Fun, List) when is_function(Fun), is_list(List) ->
quick_sort(Fun, List).

Expand All @@ -490,6 +493,52 @@ quick_sort(_Fun, []) ->
%% @private
lt(A, B) -> A < B.

%%-----------------------------------------------------------------------------
%% @param List a list
%% @returns Sorted list with duplicates removed, ordered by `<'
%% @see sort/1
%% @doc Returns a unique, sorted list, using `<' operator to determine sort order.
%% @end
%%-----------------------------------------------------------------------------
-spec usort(List :: [T]) -> [T].
usort(List) ->
Sorted = sort(List),
unique(Sorted).

%%-----------------------------------------------------------------------------
%% @param Fun sort function
%% @param List a list
%% @returns Sorted list with duplicates removed, ordered by Fun.
%% @see sort/2
%% @doc Returns a unique, sorted list.
%% @end
%%-----------------------------------------------------------------------------
-spec usort(Fun :: fun((T, T) -> boolean()), List :: [T]) -> [T].
usort(Fun, List) ->
Sorted = sort(Fun, List),
unique(Sorted, Fun).

%% @private
unique(Sorted) ->
unique(Sorted, fun(X, Y) -> X =< Y end).

%% @private
unique(Sorted, Fun) ->
unique(Sorted, Fun, []).

%% @private
unique([], _Fun, []) ->
[];
unique([X], _Fun, Acc) ->
lists:reverse([X | Acc]);
unique([X, Y | Tail], Fun, Acc) ->
case Fun(X, Y) andalso Fun(Y, X) of
true ->
unique([Y | Tail], Fun, Acc);
false ->
unique([Y | Tail], Fun, [X | Acc])
end.

%%-----------------------------------------------------------------------------
%% @param Elem the element to duplicate
%% @param Count the number of times to duplicate the element
Expand Down
27 changes: 27 additions & 0 deletions libs/etest/src/etest.erl
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,35 @@ assert_failure(F, E) ->

%% @private
run_test(Test) ->
Parent = self(),
{Pid, Ref} = spawn_opt(
fun() ->
Result = do_run_test(Test),
Parent ! {self(), Result}
end,
[monitor]
),
receive
{Pid, Result} ->
receive
{'DOWN', Ref, process, Pid, normal} -> ok
after 0 -> ok
end,
Result;
{'DOWN', Ref, process, Pid, Reason} ->
{error, Reason}
end.

do_run_test(Test) ->
try
Result = Test:test(),
Value = process_flag(trap_exit, false),
case Value of
true ->
erlang:display({test, Test, unexpected_trap_exit});
false ->
ok
end,
case erlang:system_info(machine) of
"BEAM" ->
io:format("+");
Expand Down
47 changes: 44 additions & 3 deletions src/libAtomVM/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include "smp.h"
#include "synclist.h"
#include "sys.h"
#include "term.h"
#include "utils.h"

#define IMPL_EXECUTE_LOOP
#include "opcodesswitch.h"
Expand Down Expand Up @@ -231,7 +233,32 @@ size_t context_size(Context *ctx)

bool context_get_process_info(Context *ctx, term *out, term atom_key)
{
if (UNLIKELY(memory_ensure_free(ctx, 3) != MEMORY_GC_OK)) {
size_t ret_size;
switch (atom_key) {
case HEAP_SIZE_ATOM:
case STACK_SIZE_ATOM:
case MESSAGE_QUEUE_LEN_ATOM:
case MEMORY_ATOM:
ret_size = TUPLE_SIZE(2);
break;
case LINKS_ATOM: {
struct ListHead *item;
size_t links_count = 0;
LIST_FOR_EACH (item, &ctx->monitors_head) {
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
if (monitor->ref_ticks == 0) {
links_count++;
}
}
ret_size = TUPLE_SIZE(2) + CONS_SIZE * links_count;
break;
}
default:
*out = BADARG_ATOM;
return false;
}

if (UNLIKELY(memory_ensure_free(ctx, ret_size) != MEMORY_GC_OK)) {
*out = OUT_OF_MEMORY_ATOM;
return false;
}
Expand Down Expand Up @@ -270,9 +297,23 @@ bool context_get_process_info(Context *ctx, term *out, term atom_key)
break;
}

// pids of linked processes
case LINKS_ATOM: {
term_put_tuple_element(ret, 0, LINKS_ATOM);
term list = term_nil();
struct ListHead *item;
LIST_FOR_EACH (item, &ctx->monitors_head) {
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
if (monitor->ref_ticks == 0) {
list = term_list_prepend(monitor->monitor_obj, list, &ctx->heap);
}
}
term_put_tuple_element(ret, 1, list);
break;
}

default:
*out = BADARG_ATOM;
return false;
UNREACHABLE();
}
*out = ret;
return true;
Expand Down
Loading

0 comments on commit a84426f

Please sign in to comment.