diff --git a/.github/workflows/publish-docs.yaml b/.github/workflows/publish-docs.yaml
index c8c751197..b0e6b2cde 100644
--- a/.github/workflows/publish-docs.yaml
+++ b/.github/workflows/publish-docs.yaml
@@ -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
@@ -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 }}
diff --git a/.gitignore b/.gitignore
index 45678f8f8..30aca997d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ src/platforms/esp32/sdkconfig
!src/platforms/esp32/components/libatomvm/**
.idea/**
.vscode/**
+.DS_Store
+.cache
+.clang-tidy
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 16173f0d8..b3fe3642d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
@@ -74,6 +79,8 @@ functions that default to `?ATOMVM_NVS_NS` are deprecated now).
- Fixed numerous bugs in memory allocations that could crash the VM
- Fixed SNTP support that had been broken in IDF 4.x builds
- Fixed `erlang:send/2` not sending to registered name
+- Fixed incorrect exit reason for exceptions of class exit
+- Fixed several incorrect type specifications
### Breaking Changes
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index aaa52c7a3..406f3f7bc 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -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
)
diff --git a/doc/edoc/edown_dep/rebar.config b/doc/edoc/edown_dep/rebar.config
index 207d6ffd6..82ff3eb51 100644
--- a/doc/edoc/edown_dep/rebar.config
+++ b/doc/edoc/edown_dep/rebar.config
@@ -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
]}.
diff --git a/examples/emscripten/CMakeLists.txt b/examples/emscripten/CMakeLists.txt
index d8bea697c..6856d88e8 100644
--- a/examples/emscripten/CMakeLists.txt
+++ b/examples/emscripten/CMakeLists.txt
@@ -22,7 +22,7 @@ project(examples_emscripten)
include(BuildErlang)
-pack_runnable(run_script run_script eavmlib)
+pack_runnable(run_script run_script estdlib eavmlib)
pack_runnable(call_cast call_cast eavmlib)
pack_runnable(html5_events html5_events estdlib eavmlib)
pack_runnable(wasm_webserver wasm_webserver estdlib eavmlib)
diff --git a/libs/estdlib/src/erlang.erl b/libs/estdlib/src/erlang.erl
index 05708b218..fe180515c 100644
--- a/libs/estdlib/src/erlang.erl
+++ b/libs/estdlib/src/erlang.erl
@@ -74,6 +74,8 @@
whereis/1,
spawn/1,
spawn/3,
+ spawn_link/1,
+ spawn_link/3,
spawn_opt/2,
spawn_opt/4,
link/1,
@@ -83,6 +85,8 @@
monitor/2,
demonitor/1,
demonitor/2,
+ exit/1,
+ exit/2,
open_port/2,
system_time/1,
group_leader/0,
@@ -193,12 +197,18 @@ send_after(Time, Dest, Msg) ->
%%
stack_size the number of words used in the stack (integer)
%% message_queue_len the number of messages enqueued for the process (integer)
%% memory the estimated total number of bytes in use by the process (integer)
+%% links the list of linked processes
%%
%% 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).
@@ -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
@@ -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()}
@@ -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).
@@ -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':
+%%
+%% - If target process is not trapping exits, it exits with `Reason'
+%% - If traget process is trapping exits, it receives a message
+%% ``{'EXIT', From, Reason}'' where `From' is the caller of `exit/2'.
+%%
+%% If `Reason' is `kill', the target process exits with `Reason' changed to
+%% `killed'.
+%% If `Reason' is `normal' and `Process' is not `self()':
+%%
+%% - If target process is not trapping exits, nothing happens.
+%% - If traget process is trapping exits, it receives a message
+%% ``{'EXIT', From, normal}'' where `From' is the caller of `exit/2'.
+%%
+%% If `Reason' is `normal' and `Process' is `self()':
+%%
+%% - If target process is not trapping exits, it exits with `normal'.
+%% - If traget process is trapping exits, it receives a message
+%% ``{'EXIT', From, normal}'' where `From' is the caller of `exit/2'.
+%%
+%% @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
diff --git a/libs/estdlib/src/lists.erl b/libs/estdlib/src/lists.erl
index 6c97cabbd..09f8e2697 100644
--- a/libs/estdlib/src/lists.erl
+++ b/libs/estdlib/src/lists.erl
@@ -48,6 +48,7 @@
join/2,
seq/2, seq/3,
sort/1, sort/2,
+ usort/1, usort/2,
duplicate/2,
sublist/2
]).
@@ -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).
@@ -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).
@@ -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
diff --git a/libs/etest/src/etest.erl b/libs/etest/src/etest.erl
index 9bbc0b303..20aa3bb69 100644
--- a/libs/etest/src/etest.erl
+++ b/libs/etest/src/etest.erl
@@ -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("+");
diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c
index 9c42a9117..5e21308cc 100644
--- a/src/libAtomVM/context.c
+++ b/src/libAtomVM/context.c
@@ -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"
@@ -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;
}
@@ -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;
diff --git a/src/libAtomVM/defaultatoms.c b/src/libAtomVM/defaultatoms.c
index 5981644b8..dc0035e2c 100644
--- a/src/libAtomVM/defaultatoms.c
+++ b/src/libAtomVM/defaultatoms.c
@@ -145,6 +145,10 @@ static const char *const exports_atom = "\x7" "exports";
static const char *const incomplete_atom = "\xA" "incomplete";
+static const char *const kill_atom = "\x4" "kill";
+static const char *const killed_atom = "\x6" "killed";
+static const char *const links_atom = "\x5" "links";
+
void defaultatoms_init(GlobalContext *glb)
{
int ok = 1;
@@ -274,6 +278,10 @@ void defaultatoms_init(GlobalContext *glb)
ok &= globalcontext_insert_atom(glb, incomplete_atom) == INCOMPLETE_ATOM_INDEX;
+ ok &= globalcontext_insert_atom(glb, kill_atom) == KILL_ATOM_INDEX;
+ ok &= globalcontext_insert_atom(glb, killed_atom) == KILLED_ATOM_INDEX;
+ ok &= globalcontext_insert_atom(glb, links_atom) == LINKS_ATOM_INDEX;
+
if (!ok) {
AVM_ABORT();
}
diff --git a/src/libAtomVM/defaultatoms.h b/src/libAtomVM/defaultatoms.h
index 1bf6a1198..220d72c15 100644
--- a/src/libAtomVM/defaultatoms.h
+++ b/src/libAtomVM/defaultatoms.h
@@ -154,7 +154,11 @@ extern "C" {
#define INCOMPLETE_ATOM_INDEX 99
-#define PLATFORM_ATOMS_BASE_INDEX 100
+#define KILL_ATOM_INDEX 100
+#define KILLED_ATOM_INDEX 101
+#define LINKS_ATOM_INDEX 102
+
+#define PLATFORM_ATOMS_BASE_INDEX 103
#define FALSE_ATOM TERM_FROM_ATOM_INDEX(FALSE_ATOM_INDEX)
#define TRUE_ATOM TERM_FROM_ATOM_INDEX(TRUE_ATOM_INDEX)
@@ -283,6 +287,10 @@ extern "C" {
#define INCOMPLETE_ATOM TERM_FROM_ATOM_INDEX(INCOMPLETE_ATOM_INDEX)
+#define KILL_ATOM TERM_FROM_ATOM_INDEX(KILL_ATOM_INDEX)
+#define KILLED_ATOM TERM_FROM_ATOM_INDEX(KILLED_ATOM_INDEX)
+#define LINKS_ATOM TERM_FROM_ATOM_INDEX(LINKS_ATOM_INDEX)
+
void defaultatoms_init(GlobalContext *glb);
void platform_defaultatoms_init(GlobalContext *glb);
diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c
index 4cc7a7b8a..bfb7071cb 100644
--- a/src/libAtomVM/nifs.c
+++ b/src/libAtomVM/nifs.c
@@ -37,6 +37,7 @@
#include "defaultatoms.h"
#include "dictionary.h"
#include "externalterm.h"
+#include "globalcontext.h"
#include "interop.h"
#include "mailbox.h"
#include "module.h"
@@ -121,8 +122,8 @@ static term nif_erlang_register_2(Context *ctx, int argc, term argv[]);
static term nif_erlang_unregister_1(Context *ctx, int argc, term argv[]);
static term nif_erlang_send_2(Context *ctx, int argc, term argv[]);
static term nif_erlang_setelement_3(Context *ctx, int argc, term argv[]);
-static term nif_erlang_spawn(Context *ctx, int argc, term argv[]);
-static term nif_erlang_spawn_fun(Context *ctx, int argc, term argv[]);
+static term nif_erlang_spawn_opt(Context *ctx, int argc, term argv[]);
+static term nif_erlang_spawn_fun_opt(Context *ctx, int argc, term argv[]);
static term nif_erlang_whereis_1(Context *ctx, int argc, term argv[]);
static term nif_erlang_system_time_1(Context *ctx, int argc, term argv[]);
static term nif_erlang_tuple_to_list_1(Context *ctx, int argc, term argv[]);
@@ -410,28 +411,16 @@ static const struct Nif unregister_nif =
.nif_ptr = nif_erlang_unregister_1
};
-static const struct Nif spawn_nif =
-{
- .base.type = NIFFunctionType,
- .nif_ptr = nif_erlang_spawn
-};
-
static const struct Nif spawn_opt_nif =
{
.base.type = NIFFunctionType,
- .nif_ptr = nif_erlang_spawn
-};
-
-static const struct Nif spawn_fun_nif =
-{
- .base.type = NIFFunctionType,
- .nif_ptr = nif_erlang_spawn_fun
+ .nif_ptr = nif_erlang_spawn_opt
};
static const struct Nif spawn_fun_opt_nif =
{
.base.type = NIFFunctionType,
- .nif_ptr = nif_erlang_spawn_fun
+ .nif_ptr = nif_erlang_spawn_fun_opt
};
static const struct Nif send_nif =
@@ -1081,20 +1070,98 @@ static NativeHandlerResult process_console_mailbox(Context *ctx)
return result;
}
-static term nif_erlang_spawn_fun(Context *ctx, int argc, term argv[])
+// Common handling of spawn/1, spawn/3, spawn_opt/2, spawn_opt/4
+// opts_term is [] for spawn/1,3
+static term do_spawn(Context *ctx, Context *new_ctx, term opts_term)
{
- term fun_term = argv[0];
- term opts_term = argv[1];
- VALIDATE_VALUE(fun_term, term_is_function);
+ term min_heap_size_term = interop_proplist_get_value(opts_term, MIN_HEAP_SIZE_ATOM);
+ term max_heap_size_term = interop_proplist_get_value(opts_term, MAX_HEAP_SIZE_ATOM);
+ term link_term = interop_proplist_get_value(opts_term, LINK_ATOM);
+ term monitor_term = interop_proplist_get_value(opts_term, MONITOR_ATOM);
- if (argc == 2) {
- // spawn_opt has been called
- VALIDATE_VALUE(opts_term, term_is_list);
+ if (min_heap_size_term != term_nil()) {
+ if (UNLIKELY(!term_is_integer(min_heap_size_term))) {
+ // Context was not scheduled yet, we can destroy it.
+ context_destroy(new_ctx);
+ RAISE_ERROR(BADARG_ATOM);
+ }
+ new_ctx->has_min_heap_size = 1;
+ new_ctx->min_heap_size = term_to_int(min_heap_size_term);
} else {
- // regular spawn
- opts_term = term_nil();
+ min_heap_size_term = term_from_int(0);
+ }
+ if (max_heap_size_term != term_nil()) {
+ if (UNLIKELY(!term_is_integer(max_heap_size_term))) {
+ context_destroy(new_ctx);
+ RAISE_ERROR(BADARG_ATOM);
+ }
+ new_ctx->has_max_heap_size = 1;
+ new_ctx->max_heap_size = term_to_int(max_heap_size_term);
+ }
+
+ if (new_ctx->has_min_heap_size && new_ctx->has_max_heap_size) {
+ if (term_to_int(min_heap_size_term) > term_to_int(max_heap_size_term)) {
+ context_destroy(new_ctx);
+ RAISE_ERROR(BADARG_ATOM);
+ }
}
+ uint64_t ref_ticks = 0;
+
+ if (link_term == TRUE_ATOM) {
+ if (UNLIKELY(context_link(new_ctx, term_from_local_process_id(ctx->process_id)) < 0)) {
+ context_destroy(new_ctx);
+ RAISE_ERROR(OUT_OF_MEMORY_ATOM);
+ }
+ // This is a really simple hack to get the parent - child linking
+ // I don't really like how it is implemented but it works nicely.
+ // I think it should be implemented adding a parent field to Context.
+ if (UNLIKELY(context_link(ctx, term_from_local_process_id(new_ctx->process_id)) < 0)) {
+ context_destroy(new_ctx);
+ RAISE_ERROR(OUT_OF_MEMORY_ATOM);
+ }
+ }
+ if (monitor_term == TRUE_ATOM) {
+ ref_ticks = context_monitor(new_ctx, term_from_local_process_id(ctx->process_id));
+ if (UNLIKELY(ref_ticks == 0)) {
+ context_destroy(new_ctx);
+ RAISE_ERROR(OUT_OF_MEMORY_ATOM);
+ }
+ }
+
+ term new_pid = term_from_local_process_id(new_ctx->process_id);
+
+ if (ref_ticks) {
+ int res_size = REF_SIZE + TUPLE_SIZE(2);
+ if (UNLIKELY(memory_ensure_free_opt(ctx, res_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
+ context_destroy(new_ctx);
+ RAISE_ERROR(OUT_OF_MEMORY_ATOM);
+ }
+
+ scheduler_init_ready(new_ctx);
+
+ term ref = term_from_ref_ticks(ref_ticks, &ctx->heap);
+
+ term pid_ref_tuple = term_alloc_tuple(2, &ctx->heap);
+ term_put_tuple_element(pid_ref_tuple, 0, new_pid);
+ term_put_tuple_element(pid_ref_tuple, 1, ref);
+
+ return pid_ref_tuple;
+ } else {
+ scheduler_init_ready(new_ctx);
+ return new_pid;
+ }
+}
+
+static term nif_erlang_spawn_fun_opt(Context *ctx, int argc, term argv[])
+{
+ UNUSED(argc);
+
+ term fun_term = argv[0];
+ term opts_term = argv[1];
+ VALIDATE_VALUE(fun_term, term_is_function);
+ VALIDATE_VALUE(opts_term, term_is_list);
+
Context *new_ctx = context_new(ctx->global);
new_ctx->group_leader = ctx->group_leader;
@@ -1134,22 +1201,13 @@ static term nif_erlang_spawn_fun(Context *ctx, int argc, term argv[])
new_ctx->saved_ip = fun_module->labels[label];
new_ctx->cp = module_address(fun_module->module_index, fun_module->end_instruction_ii);
- term max_heap_size_term = interop_proplist_get_value(opts_term, MAX_HEAP_SIZE_ATOM);
- if (max_heap_size_term != term_nil()) {
- new_ctx->has_max_heap_size = 1;
- //TODO: check type, make sure max_heap_size_term is an int32
- new_ctx->max_heap_size = term_to_int(max_heap_size_term);
- }
-
- term result = term_from_local_process_id(new_ctx->process_id);
-
- scheduler_init_ready(new_ctx);
-
- return result;
+ return do_spawn(ctx, new_ctx, opts_term);
}
-static term nif_erlang_spawn(Context *ctx, int argc, term argv[])
+static term nif_erlang_spawn_opt(Context *ctx, int argc, term argv[])
{
+ UNUSED(argc);
+
term module_term = argv[0];
term function_term = argv[1];
term args_term = argv[2];
@@ -1157,14 +1215,7 @@ static term nif_erlang_spawn(Context *ctx, int argc, term argv[])
VALIDATE_VALUE(module_term, term_is_atom);
VALIDATE_VALUE(function_term, term_is_atom);
VALIDATE_VALUE(args_term, term_is_list);
-
- if (argc == 4) {
- // spawn_opt has been called
- VALIDATE_VALUE(opts_term, term_is_list);
- } else {
- // regular spawn
- opts_term = term_nil();
- }
+ VALIDATE_VALUE(opts_term, term_is_list);
Context *new_ctx = context_new(ctx->global);
new_ctx->group_leader = ctx->group_leader;
@@ -1191,99 +1242,37 @@ static term nif_erlang_spawn(Context *ctx, int argc, term argv[])
new_ctx->saved_ip = found_module->labels[label];
new_ctx->cp = module_address(found_module->module_index, found_module->end_instruction_ii);
- term min_heap_size_term = interop_proplist_get_value(opts_term, MIN_HEAP_SIZE_ATOM);
- term max_heap_size_term = interop_proplist_get_value(opts_term, MAX_HEAP_SIZE_ATOM);
- term link_term = interop_proplist_get_value(opts_term, LINK_ATOM);
- term monitor_term = interop_proplist_get_value(opts_term, MONITOR_ATOM);
+ //TODO: check available registers count
+ int reg_index = 0;
+ size_t min_heap_size = 0;
+ term min_heap_size_term = interop_proplist_get_value(opts_term, MIN_HEAP_SIZE_ATOM);
if (min_heap_size_term != term_nil()) {
if (UNLIKELY(!term_is_integer(min_heap_size_term))) {
- //TODO: gracefully handle this error
- AVM_ABORT();
- }
- new_ctx->has_min_heap_size = 1;
- new_ctx->min_heap_size = term_to_int(min_heap_size_term);
- } else {
- min_heap_size_term = term_from_int(0);
- }
- if (max_heap_size_term != term_nil()) {
- if (UNLIKELY(!term_is_integer(max_heap_size_term))) {
- //TODO: gracefully handle this error
- AVM_ABORT();
- }
- new_ctx->has_max_heap_size = 1;
- new_ctx->max_heap_size = term_to_int(max_heap_size_term);
- }
-
- if (new_ctx->has_min_heap_size && new_ctx->has_max_heap_size) {
- if (term_to_int(min_heap_size_term) > term_to_int(max_heap_size_term)) {
+ // Context was not scheduled yet, we can destroy it.
+ context_destroy(new_ctx);
RAISE_ERROR(BADARG_ATOM);
}
+ min_heap_size = term_to_int(min_heap_size_term);
}
-
- uint64_t ref_ticks = 0;
-
- if (link_term == TRUE_ATOM) {
- if (UNLIKELY(context_link(new_ctx, term_from_local_process_id(ctx->process_id)) < 0)) {
- fprintf(stderr, "Unable to allocate sufficient memory to spawn process.\n");
- AVM_ABORT();
- }
- // This is a really simple hack to get the parent - child linking
- // I don't really like how it is implemented but it works nicely.
- // I think it should be implemented adding a parent field to Context.
- if (UNLIKELY(context_link(ctx, term_from_local_process_id(new_ctx->process_id)) < 0)) {
- fprintf(stderr, "Unable to allocate sufficient memory to spawn process.\n");
- AVM_ABORT();
- }
- } else if (monitor_term == TRUE_ATOM) {
- ref_ticks = context_monitor(new_ctx, term_from_local_process_id(ctx->process_id));
- if (UNLIKELY(ref_ticks == 0)) {
- fprintf(stderr, "Unable to allocate sufficient memory to spawn process.\n");
- AVM_ABORT();
- }
- }
-
- //TODO: check available registers count
- int reg_index = 0;
- term t = argv[2];
- avm_int_t size = MAX((unsigned long) term_to_int(min_heap_size_term), memory_estimate_usage(t));
+ avm_int_t size = MAX(min_heap_size, memory_estimate_usage(args_term));
if (UNLIKELY(memory_ensure_free_opt(new_ctx, size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
- //TODO: new process should be terminated, however a new pid is returned anyway
- fprintf(stderr, "Unable to allocate sufficient memory to spawn process.\n");
- AVM_ABORT();
+ // Context was not scheduled yet, we can destroy it.
+ context_destroy(new_ctx);
+ RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
- while (term_is_nonempty_list(t)) {
- new_ctx->x[reg_index] = memory_copy_term_tree(&new_ctx->heap, term_get_list_head(t));
+ while (term_is_nonempty_list(args_term)) {
+ new_ctx->x[reg_index] = memory_copy_term_tree(&new_ctx->heap, term_get_list_head(args_term));
reg_index++;
- t = term_get_list_tail(t);
- if (!term_is_list(t)) {
+ args_term = term_get_list_tail(args_term);
+ if (!term_is_list(args_term)) {
+ context_destroy(new_ctx);
RAISE_ERROR(BADARG_ATOM);
}
}
- term new_pid = term_from_local_process_id(new_ctx->process_id);
-
- scheduler_init_ready(new_ctx);
-
- if (ref_ticks) {
- int res_size = REF_SIZE + TUPLE_SIZE(2);
- if (UNLIKELY(memory_ensure_free_opt(ctx, res_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
- //TODO: new process should be terminated, however a new pid is returned anyway
- fprintf(stderr, "Unable to allocate sufficient memory to spawn process.\n");
- AVM_ABORT();
- }
-
- term ref = term_from_ref_ticks(ref_ticks, &ctx->heap);
-
- term pid_ref_tuple = term_alloc_tuple(2, &ctx->heap);
- term_put_tuple_element(pid_ref_tuple, 0, new_pid);
- term_put_tuple_element(pid_ref_tuple, 1, ref);
-
- return pid_ref_tuple;
- } else {
- return new_pid;
- }
+ return do_spawn(ctx, new_ctx, opts_term);
}
static term nif_erlang_send_2(Context *ctx, int argc, term argv[])
@@ -3144,11 +3133,48 @@ static term nif_erlang_error(Context *ctx, int argc, term argv[])
static term nif_erlang_exit(Context *ctx, int argc, term argv[])
{
- UNUSED(argc);
+ if (argc == 1) {
+ term reason = argv[0];
+ RAISE(LOWERCASE_EXIT_ATOM, reason);
+ } else {
+ term target_process = argv[0];
+ VALIDATE_VALUE(target_process, term_is_pid);
+ term reason = argv[1];
+ GlobalContext *glb = ctx->global;
+ Context *target = globalcontext_get_process_lock(glb, term_to_local_process_id(target_process));
+ bool self_is_signaled = false;
+ if (LIKELY(target)) {
+ if (reason == KILL_ATOM) {
+ mailbox_send_term_signal(target, KillSignal, KILLED_ATOM);
+ self_is_signaled = target == ctx;
+ } else {
+ if (target->trap_exit) {
+ if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(3)) != MEMORY_GC_OK)) {
+ globalcontext_get_process_unlock(glb, target);
+ RAISE_ERROR(OUT_OF_MEMORY_ATOM);
+ }
- term reason = argv[0];
- ctx->exit_reason = reason;
- RAISE(LOWERCASE_EXIT_ATOM, reason);
+ term info_tuple = term_alloc_tuple(3, &ctx->heap);
+ term_put_tuple_element(info_tuple, 0, EXIT_ATOM);
+ term_put_tuple_element(info_tuple, 1, term_from_local_process_id(ctx->process_id));
+ term_put_tuple_element(info_tuple, 2, reason);
+ mailbox_send(target, info_tuple);
+ } else if (ctx == target) {
+ mailbox_send_term_signal(target, KillSignal, reason);
+ self_is_signaled = target == ctx;
+ } else if (reason != NORMAL_ATOM){
+ mailbox_send_term_signal(target, KillSignal, reason);
+ self_is_signaled = target == ctx;
+ } // else there is no effect
+ }
+ globalcontext_get_process_unlock(glb, target);
+ }
+ if (self_is_signaled) {
+ context_update_flags(ctx, ~NoFlags, Trap);
+ return term_invalid_term();
+ }
+ return TRUE_ATOM;
+ }
}
static term nif_erlang_make_fun_3(Context *ctx, int argc, term argv[])
diff --git a/src/libAtomVM/nifs.gperf b/src/libAtomVM/nifs.gperf
index 65f7bbaf4..27b4d92bd 100644
--- a/src/libAtomVM/nifs.gperf
+++ b/src/libAtomVM/nifs.gperf
@@ -49,6 +49,7 @@ erlang:erase/1, &erase_nif
erlang:error/1, &error_nif
erlang:error/2, &error_nif
erlang:exit/1, &exit_nif
+erlang:exit/2, &exit_nif
erlang:display/1, &display_nif
erlang:float_to_binary/1, &float_to_binary_nif
erlang:float_to_binary/2, &float_to_binary_nif
@@ -81,8 +82,6 @@ erlang:register/2, ®ister_nif
erlang:unregister/1, &unregister_nif
erlang:send/2, &send_nif
erlang:setelement/3, &setelement_nif
-erlang:spawn/1, &spawn_fun_nif
-erlang:spawn/3, &spawn_nif
erlang:spawn_opt/2, &spawn_fun_opt_nif
erlang:spawn_opt/4, &spawn_opt_nif
erlang:system_info/1, &system_info_nif
diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h
index cb251044d..edc014cff 100644
--- a/src/libAtomVM/opcodesswitch.h
+++ b/src/libAtomVM/opcodesswitch.h
@@ -7163,9 +7163,14 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
}
}
- dump(ctx);
+ // Do not print crash dump if reason is normal.
+ if (ctx->x[0] != LOWERCASE_EXIT_ATOM || ctx->x[1] != NORMAL_ATOM) {
+ dump(ctx);
+ }
- {
+ if (ctx->x[0] == LOWERCASE_EXIT_ATOM) {
+ ctx->exit_reason = ctx->x[1];
+ } else {
bool throw = ctx->x[0] == THROW_ATOM;
int exit_reason_tuple_size = (throw ? TUPLE_SIZE(2) : 0) + TUPLE_SIZE(2);
@@ -7178,6 +7183,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
term_put_tuple_element(error_term, 0, NOCATCH_ATOM);
term_put_tuple_element(error_term, 1, ctx->x[1]);
} else {
+ // error
error_term = ctx->x[1];
}
diff --git a/tests/erlang_tests/CMakeLists.txt b/tests/erlang_tests/CMakeLists.txt
index 77c79b6cb..03ea7ef00 100644
--- a/tests/erlang_tests/CMakeLists.txt
+++ b/tests/erlang_tests/CMakeLists.txt
@@ -465,6 +465,8 @@ compile_erlang(link_kill_parent)
compile_erlang(link_throw)
compile_erlang(unlink_error)
compile_erlang(trap_exit_flag)
+compile_erlang(test_exit1)
+compile_erlang(test_exit2)
compile_erlang(test_stacktrace)
compile_erlang(small_big_ext)
@@ -907,6 +909,8 @@ add_custom_target(erlang_test_modules DEPENDS
link_throw.beam
unlink_error.beam
trap_exit_flag.beam
+ test_exit1.beam
+ test_exit2.beam
test_stacktrace.beam
diff --git a/tests/erlang_tests/call_with_ref_test.erl b/tests/erlang_tests/call_with_ref_test.erl
index e1e9aebe5..9e65468c8 100644
--- a/tests/erlang_tests/call_with_ref_test.erl
+++ b/tests/erlang_tests/call_with_ref_test.erl
@@ -23,7 +23,7 @@
-export([start/0, loop/1]).
start() ->
- Pid = spawn(call_with_ref_test, loop, [initial_state()]),
+ Pid = spawn_opt(call_with_ref_test, loop, [initial_state()], []),
send_integer(Pid, 1),
send_integer(Pid, 2),
send_integer(Pid, 3),
diff --git a/tests/erlang_tests/copy_terms0.erl b/tests/erlang_tests/copy_terms0.erl
index 40765175c..aec0d3147 100644
--- a/tests/erlang_tests/copy_terms0.erl
+++ b/tests/erlang_tests/copy_terms0.erl
@@ -20,10 +20,10 @@
-module(copy_terms0).
--export([start/0, loop/0, count_nestings/1]).
+-export([start/0, count_nestings/1]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), {}},
Count =
receive
diff --git a/tests/erlang_tests/copy_terms1.erl b/tests/erlang_tests/copy_terms1.erl
index 4c1f7ab89..634e328cb 100644
--- a/tests/erlang_tests/copy_terms1.erl
+++ b/tests/erlang_tests/copy_terms1.erl
@@ -20,10 +20,10 @@
-module(copy_terms1).
--export([start/0, loop/0, count_nestings/1]).
+-export([start/0, count_nestings/1]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), {{}}},
Count =
receive
diff --git a/tests/erlang_tests/copy_terms10.erl b/tests/erlang_tests/copy_terms10.erl
index 3f720b1de..a483f903d 100644
--- a/tests/erlang_tests/copy_terms10.erl
+++ b/tests/erlang_tests/copy_terms10.erl
@@ -22,7 +22,6 @@
-export([
start/0,
- loop/0,
compute/1,
compute_tree/1,
a/0,
@@ -52,7 +51,7 @@
]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), z()},
Res =
receive
diff --git a/tests/erlang_tests/copy_terms11.erl b/tests/erlang_tests/copy_terms11.erl
index 34cdec6d4..891a6aec1 100644
--- a/tests/erlang_tests/copy_terms11.erl
+++ b/tests/erlang_tests/copy_terms11.erl
@@ -20,10 +20,10 @@
-module(copy_terms11).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid !
{
self(),
diff --git a/tests/erlang_tests/copy_terms12.erl b/tests/erlang_tests/copy_terms12.erl
index ec28e73ee..1deb2c534 100644
--- a/tests/erlang_tests/copy_terms12.erl
+++ b/tests/erlang_tests/copy_terms12.erl
@@ -20,10 +20,10 @@
-module(copy_terms12).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Ref = make_ref(),
Pid !
{compute, Ref, self(),
diff --git a/tests/erlang_tests/copy_terms13.erl b/tests/erlang_tests/copy_terms13.erl
index b2bfe7fbe..2b3cae002 100644
--- a/tests/erlang_tests/copy_terms13.erl
+++ b/tests/erlang_tests/copy_terms13.erl
@@ -20,10 +20,10 @@
-module(copy_terms13).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Ref = make_ref(),
Pid !
{compute, Ref, self(),
diff --git a/tests/erlang_tests/copy_terms14.erl b/tests/erlang_tests/copy_terms14.erl
index 493796ed9..57db75dfa 100644
--- a/tests/erlang_tests/copy_terms14.erl
+++ b/tests/erlang_tests/copy_terms14.erl
@@ -20,10 +20,10 @@
-module(copy_terms14).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Ref = make_ref(),
Pid ! {det, Ref, self(), {{2, 2, 3, 9}, {1, 1, 3, 4}, {2, 0, 1, 7}, {11, 3, 4, 8}}},
Res =
diff --git a/tests/erlang_tests/copy_terms15.erl b/tests/erlang_tests/copy_terms15.erl
index 393f6b8c3..be345d199 100644
--- a/tests/erlang_tests/copy_terms15.erl
+++ b/tests/erlang_tests/copy_terms15.erl
@@ -20,11 +20,11 @@
-module(copy_terms15).
--export([start/0, sort/1, insert/2, check/1, loop/0]).
+-export([start/0, sort/1, insert/2, check/1]).
%% erlfmt-ignore
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Ref = make_ref(),
Pid ! {sort, Ref, self(), [5, 7, 9, 15, 18, 22, 4, 11, 89, 94, 1, 0, 5, 8, 9, 3, 4, 5, 35, 12, 93, 29, 39, 29,
22, 93, 23, 28, 98, 32, 32, 42, 91, 83, 38, 18, 98, 10, 12, 39, 14, 12, 93, 32, 23,
diff --git a/tests/erlang_tests/copy_terms16.erl b/tests/erlang_tests/copy_terms16.erl
index 55bd0af16..acbb60789 100644
--- a/tests/erlang_tests/copy_terms16.erl
+++ b/tests/erlang_tests/copy_terms16.erl
@@ -20,10 +20,10 @@
-module(copy_terms16).
--export([start/0, find/2, loop/0]).
+-export([start/0, find/2]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Ref = make_ref(),
Pid ! {find, Ref, self(), [<<"Hello">>, <<"Ciao">>, <<"Hola">>, <<"Hi">>, <<"Bonjur">>]},
Res =
diff --git a/tests/erlang_tests/copy_terms17.erl b/tests/erlang_tests/copy_terms17.erl
index a1278d6ef..495e22ae1 100644
--- a/tests/erlang_tests/copy_terms17.erl
+++ b/tests/erlang_tests/copy_terms17.erl
@@ -23,7 +23,7 @@
-export([start/0, sort/1, insert/2, loop/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Ref = make_ref(),
Ref2 = make_ref(),
Pid !
diff --git a/tests/erlang_tests/copy_terms18.erl b/tests/erlang_tests/copy_terms18.erl
index 30c74af4b..55768277a 100644
--- a/tests/erlang_tests/copy_terms18.erl
+++ b/tests/erlang_tests/copy_terms18.erl
@@ -22,7 +22,6 @@
-export([
start/0,
- loop/0,
compute/1,
compute_tree/1,
a/0,
@@ -51,7 +50,7 @@
]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), t()},
Res =
receive
diff --git a/tests/erlang_tests/copy_terms2.erl b/tests/erlang_tests/copy_terms2.erl
index facb365bb..734f36dfd 100644
--- a/tests/erlang_tests/copy_terms2.erl
+++ b/tests/erlang_tests/copy_terms2.erl
@@ -20,10 +20,10 @@
-module(copy_terms2).
--export([start/0, loop/0, count_nestings/1]).
+-export([start/0, count_nestings/1]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), {{{}}}},
Count =
receive
diff --git a/tests/erlang_tests/copy_terms3.erl b/tests/erlang_tests/copy_terms3.erl
index 25b2e381b..ab1ac499c 100644
--- a/tests/erlang_tests/copy_terms3.erl
+++ b/tests/erlang_tests/copy_terms3.erl
@@ -20,10 +20,10 @@
-module(copy_terms3).
--export([start/0, loop/0, count_nestings/1]).
+-export([start/0, count_nestings/1]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), {{{{{{}}}}}}},
Count =
receive
diff --git a/tests/erlang_tests/copy_terms4.erl b/tests/erlang_tests/copy_terms4.erl
index 2cb306758..ddb5ac6c0 100644
--- a/tests/erlang_tests/copy_terms4.erl
+++ b/tests/erlang_tests/copy_terms4.erl
@@ -20,10 +20,10 @@
-module(copy_terms4).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), {[{1, 0}, {2, 1}, {3, 4}], [{4, 2}, {5, 5}, {6, 1}]}},
Res =
receive
diff --git a/tests/erlang_tests/copy_terms5.erl b/tests/erlang_tests/copy_terms5.erl
index b5d9af213..96e864d1d 100644
--- a/tests/erlang_tests/copy_terms5.erl
+++ b/tests/erlang_tests/copy_terms5.erl
@@ -20,10 +20,10 @@
-module(copy_terms5).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), {[1, 2, 3], [4, 5, 6]}},
Res =
receive
diff --git a/tests/erlang_tests/copy_terms6.erl b/tests/erlang_tests/copy_terms6.erl
index 587191dc3..a041c7652 100644
--- a/tests/erlang_tests/copy_terms6.erl
+++ b/tests/erlang_tests/copy_terms6.erl
@@ -20,10 +20,10 @@
-module(copy_terms6).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), [1, 2, 3]},
Res =
receive
diff --git a/tests/erlang_tests/copy_terms7.erl b/tests/erlang_tests/copy_terms7.erl
index 41dc06b78..2e029f9b7 100644
--- a/tests/erlang_tests/copy_terms7.erl
+++ b/tests/erlang_tests/copy_terms7.erl
@@ -20,10 +20,10 @@
-module(copy_terms7).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), test, 5},
Res =
receive
diff --git a/tests/erlang_tests/copy_terms8.erl b/tests/erlang_tests/copy_terms8.erl
index 5f5befe11..6d9a6d4a2 100644
--- a/tests/erlang_tests/copy_terms8.erl
+++ b/tests/erlang_tests/copy_terms8.erl
@@ -20,10 +20,10 @@
-module(copy_terms8).
--export([start/0, loop/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {test, self(), 2},
Res =
receive
diff --git a/tests/erlang_tests/copy_terms9.erl b/tests/erlang_tests/copy_terms9.erl
index 6b708ec85..d17f94368 100644
--- a/tests/erlang_tests/copy_terms9.erl
+++ b/tests/erlang_tests/copy_terms9.erl
@@ -22,7 +22,6 @@
-export([
start/0,
- loop/0,
compute/1,
compute_tree/1,
a/0,
@@ -51,7 +50,7 @@
]).
start() ->
- Pid = spawn(?MODULE, loop, []),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), t()},
Res =
receive
diff --git a/tests/erlang_tests/gen_server_like_test.erl b/tests/erlang_tests/gen_server_like_test.erl
index d27d0eeb9..9d03b005c 100644
--- a/tests/erlang_tests/gen_server_like_test.erl
+++ b/tests/erlang_tests/gen_server_like_test.erl
@@ -76,7 +76,7 @@ start_link() ->
fake_start() ->
{ok, InitialState} = init(initial_state()),
- spawn(?MODULE, loop, [InitialState]).
+ spawn_opt(?MODULE, loop, [InitialState], []).
init(Initial) ->
{ok, Initial}.
diff --git a/tests/erlang_tests/guards3.erl b/tests/erlang_tests/guards3.erl
index b42120b0b..c42795b36 100644
--- a/tests/erlang_tests/guards3.erl
+++ b/tests/erlang_tests/guards3.erl
@@ -24,7 +24,7 @@
start() ->
Port = do_open_port("echo", []),
- Pid = spawn(guards3, loop, [initial_state()]),
+ Pid = spawn_opt(guards3, loop, [initial_state()], []),
do_something(Port) + do_something(Pid) * 3 + do_something(2) * 100.
do_open_port(PortName, Param) ->
diff --git a/tests/erlang_tests/is_type.erl b/tests/erlang_tests/is_type.erl
index 6d967049f..4c30e9dbf 100644
--- a/tests/erlang_tests/is_type.erl
+++ b/tests/erlang_tests/is_type.erl
@@ -20,10 +20,10 @@
-module(is_type).
--export([start/0, test_is_type/8, all_true/8, quick_exit/0]).
+-export([start/0, test_is_type/8, all_true/8]).
start() ->
- Pid = spawn(?MODULE, quick_exit, []),
+ Pid = self(),
test_is_type(hello, <<"hello">>, 10, [1, 2, 3], 5, Pid, make_ref(), {1, 2}).
test_is_type(A, B, I, L, N, P, R, T) ->
@@ -44,6 +44,3 @@ all_true(false, false, false, false, false, false, false, false) ->
0;
all_true(_, _, _, _, _, _, _, _) ->
-1.
-
-quick_exit() ->
- ok.
diff --git a/tests/erlang_tests/link_kill_parent.erl b/tests/erlang_tests/link_kill_parent.erl
index 83c7566c2..7df586b64 100644
--- a/tests/erlang_tests/link_kill_parent.erl
+++ b/tests/erlang_tests/link_kill_parent.erl
@@ -20,10 +20,10 @@
-module(link_kill_parent).
--export([start/0, start2/0, sum/1, proc/1]).
+-export([start/0, sum/1, proc/1]).
start() ->
- Pid = spawn(?MODULE, start2, []),
+ Pid = spawn_opt(fun start2/0, []),
Pid ! {monitor, self()},
CP =
receive
diff --git a/tests/erlang_tests/link_throw.erl b/tests/erlang_tests/link_throw.erl
index af4d7ae72..f4e0aaa5c 100644
--- a/tests/erlang_tests/link_throw.erl
+++ b/tests/erlang_tests/link_throw.erl
@@ -20,10 +20,10 @@
-module(link_throw).
--export([start/0, start2/0, sum/1, proc/1]).
+-export([start/0, sum/1, proc/1]).
start() ->
- {Pid, Ref} = spawn_opt(?MODULE, start2, [], [monitor]),
+ {Pid, Ref} = spawn_opt(fun start2/0, [monitor]),
Pid ! {self(), sum},
CM =
receive
@@ -52,7 +52,7 @@ start2() ->
receive
{ParentPid, sum} -> ParentPid
end,
- Pid = spawn(?MODULE, proc, [L]),
+ Pid = spawn_opt(?MODULE, proc, [L], []),
erlang:link(Pid),
Pid ! {self(), sum},
receive
diff --git a/tests/erlang_tests/pingpong.erl b/tests/erlang_tests/pingpong.erl
index 525555155..d2266b645 100644
--- a/tests/erlang_tests/pingpong.erl
+++ b/tests/erlang_tests/pingpong.erl
@@ -24,8 +24,8 @@
start() ->
Echo = echo:start(),
- Pong = spawn(?MODULE, pong, [Echo, self()]),
- spawn(?MODULE, ping, [Echo, Pong]),
+ Pong = spawn_opt(?MODULE, pong, [Echo, self()], []),
+ spawn_opt(?MODULE, ping, [Echo, Pong], []),
receive
exit -> 1;
_Any -> 0
diff --git a/tests/erlang_tests/prime.erl b/tests/erlang_tests/prime.erl
index 5e2118f09..4144ebb47 100644
--- a/tests/erlang_tests/prime.erl
+++ b/tests/erlang_tests/prime.erl
@@ -23,9 +23,9 @@
-export([start/0, is_prime/1, calculate_list/2]).
start() ->
- spawn(prime, calculate_list, num_range(2, 100)),
- spawn(prime, calculate_list, num_range(100, 400)),
- spawn(prime, calculate_list, num_range(500, 1500)),
+ spawn_opt(prime, calculate_list, num_range(2, 100), []),
+ spawn_opt(prime, calculate_list, num_range(100, 400), []),
+ spawn_opt(prime, calculate_list, num_range(500, 1500), []),
all_primes_test(2000) -
all_primes_test(2000) +
all_primes_test(2000) -
diff --git a/tests/erlang_tests/prime_ext.erl b/tests/erlang_tests/prime_ext.erl
index 0db5b7bea..d82a1cf0c 100644
--- a/tests/erlang_tests/prime_ext.erl
+++ b/tests/erlang_tests/prime_ext.erl
@@ -30,9 +30,9 @@
]).
start() ->
- spawn(?MODULE, calculate_list, num_range(2, 100)),
- spawn(?MODULE, calculate_list, num_range(100, 400)),
- spawn(?MODULE, calculate_list, num_range(500, 1500)),
+ spawn_opt(?MODULE, calculate_list, num_range(2, 100), []),
+ spawn_opt(?MODULE, calculate_list, num_range(100, 400), []),
+ spawn_opt(?MODULE, calculate_list, num_range(500, 1500), []),
?MODULE:all_primes_test(2000) -
?MODULE:all_primes_test(2000) +
?MODULE:all_primes_test(2000) -
diff --git a/tests/erlang_tests/send_to_dead_process.erl b/tests/erlang_tests/send_to_dead_process.erl
index 15995fef9..9d43eba3e 100644
--- a/tests/erlang_tests/send_to_dead_process.erl
+++ b/tests/erlang_tests/send_to_dead_process.erl
@@ -20,10 +20,10 @@
-module(send_to_dead_process).
--export([start/0, double_and_terminate/0]).
+-export([start/0]).
start() ->
- Pid = spawn(?MODULE, double_and_terminate, []),
+ Pid = spawn_opt(fun double_and_terminate/0, []),
Pid ! {self(), 10},
Result =
receive
diff --git a/tests/erlang_tests/sleep.erl b/tests/erlang_tests/sleep.erl
index 9a749b48c..8601d4849 100644
--- a/tests/erlang_tests/sleep.erl
+++ b/tests/erlang_tests/sleep.erl
@@ -42,10 +42,13 @@ spawn_sleeping_processes([], Pids) ->
Pids;
spawn_sleeping_processes([Timeout | T], Acc) ->
Parent = self(),
- Pid = spawn(fun() ->
- ok = sub_sleep(Timeout),
- Parent ! {self(), ok}
- end),
+ Pid = spawn_opt(
+ fun() ->
+ ok = sub_sleep(Timeout),
+ Parent ! {self(), ok}
+ end,
+ []
+ ),
spawn_sleeping_processes(T, [Pid | Acc]).
wait_for_sleeping_processes([]) ->
diff --git a/tests/erlang_tests/spawn_fun1.erl b/tests/erlang_tests/spawn_fun1.erl
index 479dc89e9..54da47a19 100644
--- a/tests/erlang_tests/spawn_fun1.erl
+++ b/tests/erlang_tests/spawn_fun1.erl
@@ -23,7 +23,7 @@
-export([start/0, loop/0]).
start() ->
- Pid = spawn(fun loop/0),
+ Pid = spawn_opt(fun loop/0, []),
Pid ! {self(), 21},
Double =
receive
diff --git a/tests/erlang_tests/spawn_fun2.erl b/tests/erlang_tests/spawn_fun2.erl
index c1fa71093..70b2e9c29 100644
--- a/tests/erlang_tests/spawn_fun2.erl
+++ b/tests/erlang_tests/spawn_fun2.erl
@@ -25,7 +25,7 @@
start() ->
Father = self(),
Fun = fun() -> Father ! 33 end,
- spawn(Fun),
+ spawn_opt(Fun, []),
Result =
receive
Any -> Any
diff --git a/tests/erlang_tests/spawn_fun3.erl b/tests/erlang_tests/spawn_fun3.erl
index ca0895cbb..9ed52ab35 100644
--- a/tests/erlang_tests/spawn_fun3.erl
+++ b/tests/erlang_tests/spawn_fun3.erl
@@ -30,7 +30,7 @@ start() ->
Pid ! sum(L)
end
end,
- Pid = spawn(Fun),
+ Pid = spawn_opt(Fun, []),
Pid ! {self(), sum},
receive
N -> N
diff --git a/tests/erlang_tests/spawn_opt_link_normal.erl b/tests/erlang_tests/spawn_opt_link_normal.erl
index f4f76beee..607c9c675 100644
--- a/tests/erlang_tests/spawn_opt_link_normal.erl
+++ b/tests/erlang_tests/spawn_opt_link_normal.erl
@@ -20,10 +20,10 @@
-module(spawn_opt_link_normal).
--export([start/0, start2/0, sum/1, proc/1]).
+-export([start/0, sum/1, proc/1]).
start() ->
- {Pid, Ref} = spawn_opt(?MODULE, start2, [], [monitor]),
+ {Pid, Ref} = spawn_opt(fun start2/0, [monitor]),
CM =
receive
N2 -> N2
diff --git a/tests/erlang_tests/state_test.erl b/tests/erlang_tests/state_test.erl
index b99307db7..41cf8df88 100644
--- a/tests/erlang_tests/state_test.erl
+++ b/tests/erlang_tests/state_test.erl
@@ -23,7 +23,7 @@
-export([start/0, loop/1]).
start() ->
- Pid = spawn(state_test, loop, [initial_state()]),
+ Pid = spawn_opt(state_test, loop, [initial_state()], []),
send_integer(Pid, 1),
send_integer(Pid, 2),
send_integer(Pid, 3),
diff --git a/tests/erlang_tests/state_test2.erl b/tests/erlang_tests/state_test2.erl
index dc611a6b1..031b33cb7 100644
--- a/tests/erlang_tests/state_test2.erl
+++ b/tests/erlang_tests/state_test2.erl
@@ -23,7 +23,7 @@
-export([start/0, loop/1]).
start() ->
- Pid = spawn(state_test2, loop, [initial_state()]),
+ Pid = spawn_opt(state_test2, loop, [initial_state()], []),
state_test2_sender:send_msgs(Pid).
initial_state() ->
diff --git a/tests/erlang_tests/state_test3.erl b/tests/erlang_tests/state_test3.erl
index 7aae7c19b..9e548fdb3 100644
--- a/tests/erlang_tests/state_test3.erl
+++ b/tests/erlang_tests/state_test3.erl
@@ -23,7 +23,7 @@
-export([start/0]).
start() ->
- Pid = spawn(state_test3_server, loop, [initial_state()]),
+ Pid = spawn_opt(state_test3_server, loop, [initial_state()], []),
send_integer(Pid, 1),
send_integer(Pid, 2),
send_integer(Pid, 3),
diff --git a/tests/erlang_tests/test_bs.erl b/tests/erlang_tests/test_bs.erl
index 96e0f2f26..c7c3a0787 100644
--- a/tests/erlang_tests/test_bs.erl
+++ b/tests/erlang_tests/test_bs.erl
@@ -20,7 +20,7 @@
-module(test_bs).
--export([start/0]).
+-export([start/0, id/1]).
start() ->
test_pack_small_ints({2, 61, 20}, <<23, 180>>),
@@ -368,12 +368,4 @@ test_large() ->
true = id(X) =:= <<42:1024>>,
ok.
-% Prevent optimization from the compiler
-id(X) ->
- Self = self(),
- Pid = spawn(fun() ->
- Self ! {self(), X}
- end),
- receive
- {Pid, R} -> R
- end.
+id(X) -> X.
diff --git a/tests/erlang_tests/test_exit1.erl b/tests/erlang_tests/test_exit1.erl
new file mode 100644
index 000000000..c0f53fc95
--- /dev/null
+++ b/tests/erlang_tests/test_exit1.erl
@@ -0,0 +1,167 @@
+%
+% This file is part of AtomVM.
+%
+% Copyright 2023 Paul Guyot
+%
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+%
+% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
+%
+
+-module(test_exit1).
+
+-export([start/0]).
+
+start() ->
+ ok = test_catch(),
+ ok = test_nocatch(),
+ ok = test_trap_kill(),
+ 0.
+
+test_catch() ->
+ ok =
+ try
+ exit(normal),
+ fail
+ catch
+ exit:normal -> ok
+ end,
+ ok =
+ try
+ exit(foo),
+ fail
+ catch
+ exit:foo -> ok
+ end,
+ ok =
+ try
+ exit(kill),
+ fail
+ catch
+ exit:kill -> ok
+ end,
+ ok.
+
+test_nocatch() ->
+ {Pid1, Ref1} = spawn_opt(
+ fun() ->
+ exit(normal)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref1, process, Pid1, normal} -> ok;
+ Other -> Other
+ after 500 -> timeout
+ end,
+ {Pid2, Ref2} = spawn_opt(
+ fun() ->
+ exit(foo)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref2, process, Pid2, foo} -> ok
+ after 500 -> timeout
+ end,
+ {Pid3, Ref3} = spawn_opt(
+ fun() ->
+ exit(kill)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref3, process, Pid3, kill} -> ok
+ after 500 -> timeout
+ end,
+ {Pid4, Ref4} = spawn_opt(
+ fun() ->
+ process_flag(trap_exit, true),
+ exit(normal)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref4, process, Pid4, normal} -> ok
+ after 500 -> timeout
+ end,
+ {Pid5, Ref5} = spawn_opt(
+ fun() ->
+ process_flag(trap_exit, true),
+ exit(foo)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref5, process, Pid5, foo} -> ok
+ after 500 -> timeout
+ end,
+ {Pid6, Ref6} = spawn_opt(
+ fun() ->
+ process_flag(trap_exit, true),
+ exit(kill)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref6, process, Pid6, kill} -> ok
+ after 500 -> timeout
+ end,
+ ok.
+
+test_trap_kill() ->
+ process_flag(trap_exit, true),
+ {Pid1, Ref1} = spawn_opt(
+ fun() ->
+ exit(kill)
+ end,
+ [monitor, link]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref1, process, Pid1, kill} -> ok
+ after 500 -> timeout
+ end,
+ ok =
+ receive
+ {'EXIT', Pid1, kill} -> ok
+ after 500 -> timeout
+ end,
+ {Pid2, Ref2} = spawn_opt(
+ fun() ->
+ spawn_opt(
+ fun() ->
+ exit(kill)
+ end,
+ [link]
+ ),
+ % wait to be killed by link
+ ok =
+ receive
+ after 500 -> timeout
+ end
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref2, process, Pid2, kill} -> ok
+ after 500 -> timeout
+ end,
+ ok.
diff --git a/tests/erlang_tests/test_exit2.erl b/tests/erlang_tests/test_exit2.erl
new file mode 100644
index 000000000..5a45c319b
--- /dev/null
+++ b/tests/erlang_tests/test_exit2.erl
@@ -0,0 +1,270 @@
+%
+% This file is part of AtomVM.
+%
+% Copyright 2023 Paul Guyot
+%
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+%
+% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
+%
+
+-module(test_exit2).
+
+-export([start/0]).
+
+start() ->
+ ok = test_reason_other(),
+ ok = test_reason_kill(),
+ ok = test_reason_normal_not_self(),
+ ok = test_reason_normal_self(),
+ ok = test_exit_dead(),
+ 0.
+
+test_reason_other() ->
+ {Pid1, Ref1} = spawn_opt(
+ fun() ->
+ receive
+ after 500 -> ok
+ end
+ end,
+ [monitor]
+ ),
+ true = exit(Pid1, other),
+ ok =
+ receive
+ {'DOWN', Ref1, process, Pid1, other} -> ok
+ after 500 -> timeout
+ end,
+ Parent = self(),
+ Pid2 = spawn_opt(
+ fun() ->
+ process_flag(trap_exit, true),
+ Parent ! {self(), ready},
+ ok =
+ receive
+ {'EXIT', _From, _Reason} = ExitMessage ->
+ Parent ! {self(), ExitMessage},
+ ok;
+ Other ->
+ Parent ! {self(), {unexpected, Other}}
+ end
+ end,
+ []
+ ),
+ ok =
+ receive
+ {Pid2, ready} -> ok
+ after 500 -> timeout
+ end,
+ true = exit(Pid2, other),
+ ok =
+ receive
+ {Pid2, {'EXIT', Parent, other}} -> ok;
+ Other -> Other
+ after 500 -> timeout
+ end,
+ ok.
+
+test_reason_kill() ->
+ {Pid1, Ref1} = spawn_opt(
+ fun() ->
+ receive
+ after 500 -> ok
+ end
+ end,
+ [monitor]
+ ),
+ true = exit(Pid1, kill),
+ ok =
+ receive
+ {'DOWN', Ref1, process, Pid1, killed} -> ok
+ after 500 -> timeout
+ end,
+ Parent = self(),
+ {Pid2, Ref2} = spawn_opt(
+ fun() ->
+ process_flag(trap_exit, true),
+ Parent ! {self(), ready},
+ receive
+ after 500 -> ok
+ end
+ end,
+ [monitor]
+ ),
+ {Pid3, Ref3} = spawn_opt(
+ fun() ->
+ link(Pid2),
+ Parent ! {self(), ready},
+ receive
+ after 500 -> ok
+ end
+ end,
+ [monitor]
+ ),
+ {Pid4, Ref4} = spawn_opt(
+ fun() ->
+ link(Pid2),
+ process_flag(trap_exit, true),
+ Parent ! {self(), ready},
+ receive
+ {'EXIT', _From, _Reason} = ExitMessage -> Parent ! {self(), ExitMessage}
+ after 500 -> ok
+ end
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {Pid2, ready} -> ok
+ after 500 -> timeout
+ end,
+ ok =
+ receive
+ {Pid3, ready} -> ok
+ after 500 -> timeout
+ end,
+ ok =
+ receive
+ {Pid4, ready} -> ok
+ after 500 -> timeout
+ end,
+ true = exit(Pid2, kill),
+ ok =
+ receive
+ {'DOWN', Ref2, process, Pid2, killed} -> ok
+ after 500 -> timeout
+ end,
+ ok =
+ receive
+ {'DOWN', Ref3, process, Pid3, killed} -> ok
+ after 500 -> timeout
+ end,
+ ok =
+ receive
+ {'DOWN', Ref4, process, Pid4, normal} -> ok
+ after 500 -> timeout
+ end,
+ Pid2 =
+ receive
+ {Pid4, {'EXIT', Pid, killed}} -> Pid
+ after 500 -> timeout
+ end,
+ ok.
+
+test_reason_normal_not_self() ->
+ {Pid1, Ref1} = spawn_opt(
+ fun() ->
+ receive
+ {Caller, ping} -> Caller ! {self(), pong}
+ after 500 -> ok
+ end
+ end,
+ [monitor]
+ ),
+ true = exit(Pid1, normal),
+ Pid1 ! {self(), ping},
+ ok =
+ receive
+ {Pid1, pong} -> ok
+ after 500 -> timeout
+ end,
+ ok =
+ receive
+ {'DOWN', Ref1, process, Pid1, normal} -> ok
+ after 500 -> timeout
+ end,
+ Parent = self(),
+ Pid2 = spawn_opt(
+ fun() ->
+ process_flag(trap_exit, true),
+ Parent ! {self(), ready},
+ ok =
+ receive
+ {'EXIT', _From, _Reason} = ExitMessage ->
+ Parent ! {self(), ExitMessage},
+ ok;
+ Other ->
+ Parent ! {self(), {unexpected, Other}}
+ end
+ end,
+ []
+ ),
+ ok =
+ receive
+ {Pid2, ready} -> ok
+ after 500 -> timeout
+ end,
+ true = exit(Pid2, normal),
+ ok =
+ receive
+ {Pid2, {'EXIT', Parent, normal}} -> ok;
+ Other -> Other
+ after 500 -> timeout
+ end,
+ ok.
+
+test_reason_normal_self() ->
+ Parent = self(),
+ {Pid1, Ref1} = spawn_opt(
+ fun() ->
+ exit(self(), normal),
+ Parent ! {self(), unexpected},
+ exit(unexpected)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {'DOWN', Ref1, process, Pid1, normal} -> ok
+ after 500 -> timeout
+ end,
+ {Pid2, Ref2} = spawn_opt(
+ fun() ->
+ process_flag(trap_exit, true),
+ exit(self(), normal),
+ ok =
+ receive
+ {'EXIT', _From, _Reason} = ExitMessage ->
+ Parent ! {self(), ExitMessage},
+ ok;
+ Other ->
+ Parent ! {self(), {unexpected, Other}}
+ end,
+ exit(expected)
+ end,
+ [monitor]
+ ),
+ ok =
+ receive
+ {Pid2, {'EXIT', Pid2, normal}} -> ok;
+ Other -> Other
+ after 500 -> timeout
+ end,
+ ok =
+ receive
+ {'DOWN', Ref2, process, Pid2, expected} -> ok
+ after 500 -> timeout
+ end,
+ ok.
+
+test_exit_dead() ->
+ {Pid1, Ref1} = spawn_opt(fun() -> ok end, [monitor]),
+ ok =
+ receive
+ {'DOWN', Ref1, process, Pid1, normal} -> ok
+ after 500 -> timeout
+ end,
+ true = exit(Pid1, normal),
+ true = exit(Pid1, kill),
+ true = exit(Pid1, other),
+ ok.
diff --git a/tests/erlang_tests/test_is_process_alive.erl b/tests/erlang_tests/test_is_process_alive.erl
index 84fb5b932..a397f0862 100644
--- a/tests/erlang_tests/test_is_process_alive.erl
+++ b/tests/erlang_tests/test_is_process_alive.erl
@@ -20,18 +20,22 @@
-module(test_is_process_alive).
--export([start/0, fact/1, no_loop/0]).
+-export([start/0, fact/1]).
start() ->
- Pid = spawn(?MODULE, no_loop, []),
+ {Pid, Monitor} = spawn_opt(fun no_loop/0, [monitor]),
A = g(is_process_alive(Pid)),
Pid ! {self(), 5},
Fact =
receive
- Result ->
+ {Pid, Result} ->
Result
end,
- sleep(50),
+ ok =
+ receive
+ {'DOWN', Monitor, process, Pid, normal} -> ok
+ after 500 -> timeout
+ end,
case is_process_alive(Pid) of
false ->
Fact + A;
@@ -42,9 +46,9 @@ start() ->
no_loop() ->
receive
{Pid, AnyInteger} when is_integer(AnyInteger) ->
- Pid ! fact(AnyInteger);
+ Pid ! {self(), fact(AnyInteger)};
{Pid, _AnyVal} ->
- Pid ! error
+ Pid ! {self(), error}
end.
fact(0) ->
@@ -52,11 +56,6 @@ fact(0) ->
fact(N) ->
N * fact(N - 1).
-sleep(MSecs) ->
- receive
- after MSecs -> ok
- end.
-
g(true) ->
1;
g(false) ->
diff --git a/tests/erlang_tests/test_min_heap_size.erl b/tests/erlang_tests/test_min_heap_size.erl
index f27641437..de7ba3816 100644
--- a/tests/erlang_tests/test_min_heap_size.erl
+++ b/tests/erlang_tests/test_min_heap_size.erl
@@ -24,7 +24,7 @@
start() ->
Self = self(),
- Pid1 = spawn(?MODULE, loop, [Self]),
+ Pid1 = spawn_opt(?MODULE, loop, [Self], []),
receive
ok -> ok
end,
diff --git a/tests/erlang_tests/test_monitor.erl b/tests/erlang_tests/test_monitor.erl
index d0e6e3532..a36890543 100644
--- a/tests/erlang_tests/test_monitor.erl
+++ b/tests/erlang_tests/test_monitor.erl
@@ -33,7 +33,7 @@ start() ->
0.
test_monitor_normal() ->
- Pid = spawn(fun() -> normal_loop() end),
+ Pid = spawn_opt(fun() -> normal_loop() end, []),
Ref = monitor(process, Pid),
Pid ! {self(), quit},
ok =
@@ -51,7 +51,7 @@ test_monitor_normal() ->
ok.
test_monitor_demonitor() ->
- Pid = spawn(fun() -> normal_loop() end),
+ Pid = spawn_opt(fun() -> normal_loop() end, []),
Ref = monitor(process, Pid),
true = demonitor(Ref),
Pid ! {self(), quit},
@@ -69,10 +69,12 @@ test_monitor_demonitor() ->
ok.
test_monitor_noproc() ->
- Pid = spawn(fun() -> ok end),
- receive
- after 100 -> ok
- end,
+ {Pid, Monitor} = spawn_opt(fun() -> ok end, [monitor]),
+ ok =
+ receive
+ {'DOWN', Monitor, process, Pid, normal} -> ok
+ after 500 -> timeout
+ end,
Ref = monitor(process, Pid),
ok =
receive
@@ -83,7 +85,7 @@ test_monitor_noproc() ->
ok.
test_monitor_demonitor_flush() ->
- Pid = spawn(fun() -> normal_loop() end),
+ Pid = spawn_opt(fun() -> normal_loop() end, []),
Ref = monitor(process, Pid),
Pid ! {self(), quit},
receive
@@ -104,7 +106,7 @@ test_monitor_demonitor_flush() ->
ok.
test_monitor_demonitor_info() ->
- Pid = spawn(fun() -> normal_loop() end),
+ Pid = spawn_opt(fun() -> normal_loop() end, []),
Ref = monitor(process, Pid),
true = demonitor(Ref, [info]),
Pid ! {self(), quit},
@@ -123,7 +125,7 @@ test_monitor_demonitor_info() ->
ok.
test_monitor_demonitor_flush_info_true() ->
- Pid = spawn(fun() -> normal_loop() end),
+ Pid = spawn_opt(fun() -> normal_loop() end, []),
Ref = monitor(process, Pid),
Pid ! {self(), quit},
receive
@@ -144,7 +146,7 @@ test_monitor_demonitor_flush_info_true() ->
ok.
test_monitor_demonitor_flush_info_false() ->
- Pid = spawn(fun() -> normal_loop() end),
+ Pid = spawn_opt(fun() -> normal_loop() end, []),
Ref = monitor(process, Pid),
true = demonitor(Ref, [flush, info]),
Pid ! {self(), quit},
diff --git a/tests/erlang_tests/test_pids_ordering.erl b/tests/erlang_tests/test_pids_ordering.erl
index 16f2a146d..678743cf7 100644
--- a/tests/erlang_tests/test_pids_ordering.erl
+++ b/tests/erlang_tests/test_pids_ordering.erl
@@ -23,12 +23,12 @@
-export([start/0, sort/1, insert/2, check/2]).
start() ->
- P1 = spawn(?MODULE, sort, [[]]),
- P2 = spawn(?MODULE, sort, [[1]]),
- P3 = spawn(?MODULE, sort, [[2, 1]]),
- P4 = spawn(?MODULE, sort, [[3]]),
- P5 = spawn(?MODULE, sort, [[4, 1]]),
- P6 = spawn(?MODULE, sort, [[]]),
+ P1 = spawn_opt(?MODULE, sort, [[]], []),
+ P2 = spawn_opt(?MODULE, sort, [[1]], []),
+ P3 = spawn_opt(?MODULE, sort, [[2, 1]], []),
+ P4 = spawn_opt(?MODULE, sort, [[3]], []),
+ P5 = spawn_opt(?MODULE, sort, [[4, 1]], []),
+ P6 = spawn_opt(?MODULE, sort, [[]], []),
Sorted = sort([P3, P4, P1, P5, P2]),
check(Sorted, [P1, P2, P3, P4, P5]) +
bool_to_n(Sorted < [P6]) * 2 +
diff --git a/tests/erlang_tests/test_process_info.erl b/tests/erlang_tests/test_process_info.erl
index 464b88aa0..987898619 100644
--- a/tests/erlang_tests/test_process_info.erl
+++ b/tests/erlang_tests/test_process_info.erl
@@ -29,6 +29,11 @@ start() ->
ok -> ok
end,
test_message_queue_len(Pid, Self),
+ {links, []} = process_info(Pid, links),
+ link(Pid),
+ {links, [Self]} = process_info(Pid, links),
+ unlink(Pid),
+ {links, []} = process_info(Pid, links),
Pid ! {Self, stop},
_Accum =
receive
diff --git a/tests/erlang_tests/test_raise.erl b/tests/erlang_tests/test_raise.erl
index 048204cab..9b4b16eb5 100644
--- a/tests/erlang_tests/test_raise.erl
+++ b/tests/erlang_tests/test_raise.erl
@@ -23,7 +23,7 @@
-export([start/0]).
start() ->
- Pid = spawn(fun() -> loop(0) end),
+ Pid = spawn_opt(fun() -> loop(0) end, []),
Tick = fun() -> Pid ! tick end,
foo = tryit(
fun() -> foo end,
diff --git a/tests/erlang_tests/test_refc_binaries.erl b/tests/erlang_tests/test_refc_binaries.erl
index a700968b9..ec26bc2ac 100644
--- a/tests/erlang_tests/test_refc_binaries.erl
+++ b/tests/erlang_tests/test_refc_binaries.erl
@@ -106,7 +106,7 @@ test_send() ->
Bin = create_binary(1024),
?VERIFY(MemoryBinarySize + 1024 =< erlang:memory(binary)),
- Pid = erlang:spawn(fun() -> loop(#state{}) end),
+ Pid = spawn_opt(fun() -> loop(#state{}) end, []),
PidHeapSize0 = get_heap_size(Pid),
%%
%% Send the process a refc binary, and check heap size
@@ -134,7 +134,7 @@ test_spawn() ->
%%
%% Spawn a function, passing a refc binary through the args
%%
- Pid = erlang:spawn(?MODULE, loop, [#state{bin = Bin}]),
+ Pid = spawn_opt(?MODULE, loop, [#state{bin = Bin}], []),
PidHeapSize0 = get_heap_size(Pid),
%%
%% Make sure we can get what we spawned
@@ -154,7 +154,7 @@ test_spawn_fun() ->
%%
%% Spawn a function, passing a refc binary through the args
%%
- Pid = erlang:spawn(fun() -> loop(#state{bin = Bin}) end),
+ Pid = spawn_opt(fun() -> loop(#state{bin = Bin}) end, []),
PidHeapSize0 = get_heap_size(Pid),
%%
%% Make sure we can get what we spawned
@@ -227,7 +227,7 @@ create_string(N, Accum) ->
run_test(Fun) ->
Self = self(),
- _Pid = spawn(fun() -> execute(Self, Fun) end),
+ _Pid = spawn_opt(fun() -> execute(Self, Fun) end, []),
receive
ok ->
ok;
diff --git a/tests/erlang_tests/test_selective_receive.erl b/tests/erlang_tests/test_selective_receive.erl
index b38b6ec5a..d07c5bfa0 100644
--- a/tests/erlang_tests/test_selective_receive.erl
+++ b/tests/erlang_tests/test_selective_receive.erl
@@ -29,21 +29,30 @@ start() ->
test_selective_receive() ->
Self = self(),
- spawn(fun() ->
- Self ! three
- end),
- spawn(fun() ->
- receive
- after 250 -> ok
+ spawn_opt(
+ fun() ->
+ Self ! three
end,
- Self ! two
- end),
- spawn(fun() ->
- receive
- after 500 -> ok
+ []
+ ),
+ spawn_opt(
+ fun() ->
+ receive
+ after 250 -> ok
+ end,
+ Self ! two
end,
- Self ! one
- end),
+ []
+ ),
+ spawn_opt(
+ fun() ->
+ receive
+ after 500 -> ok
+ end,
+ Self ! one
+ end,
+ []
+ ),
receive
one ->
ok
@@ -59,15 +68,21 @@ test_selective_receive() ->
test_selective_receive_with_timeout() ->
Self = self(),
- spawn(fun() ->
- Self ! two
- end),
- spawn(fun() ->
- receive
- after 500 -> ok
+ spawn_opt(
+ fun() ->
+ Self ! two
+ end,
+ []
+ ),
+ spawn_opt(
+ fun() ->
+ receive
+ after 500 -> ok
+ end,
+ Self ! one
end,
- Self ! one
- end),
+ []
+ ),
ok =
receive
one ->
diff --git a/tests/erlang_tests/test_send.erl b/tests/erlang_tests/test_send.erl
index 6fda38ed3..dc802de52 100644
--- a/tests/erlang_tests/test_send.erl
+++ b/tests/erlang_tests/test_send.erl
@@ -23,7 +23,12 @@
-export([start/0]).
start() ->
- Dead = spawn(fun() -> ok end),
+ {Dead, DeadMonitor} = spawn_opt(fun() -> ok end, [monitor]),
+ ok =
+ receive
+ {'DOWN', DeadMonitor, process, Dead, normal} -> ok
+ after 500 -> timeout
+ end,
Sent = send(self(), 32),
receive
Sent ->
diff --git a/tests/erlang_tests/test_stacktrace.erl b/tests/erlang_tests/test_stacktrace.erl
index 8655f4a71..135a67723 100644
--- a/tests/erlang_tests/test_stacktrace.erl
+++ b/tests/erlang_tests/test_stacktrace.erl
@@ -190,7 +190,7 @@ test_body_recursive_throw() ->
test_spawned_throw() ->
Self = self(),
- spawn(
+ spawn_opt(
fun() ->
try
do_some_stuff(blah),
@@ -208,7 +208,8 @@ test_spawned_throw() ->
),
Self ! Result
end
- end
+ end,
+ []
),
receive
Result ->
diff --git a/tests/erlang_tests/test_sub_binaries.erl b/tests/erlang_tests/test_sub_binaries.erl
index 2490ba016..f5d7a80b3 100644
--- a/tests/erlang_tests/test_sub_binaries.erl
+++ b/tests/erlang_tests/test_sub_binaries.erl
@@ -137,7 +137,7 @@ test_send_sub_binary() ->
Bin = create_binary(1024),
BinarySize = erlang:byte_size(Bin),
?VERIFY(MemoryBinarySize + 1024 == erlang:memory(binary)),
- Pid = erlang:spawn(fun() -> loop(#state{}) end),
+ Pid = spawn_opt(fun() -> loop(#state{}) end, []),
PidHeapSize0 = get_heap_size(Pid),
%%
%% Send the process a refc binary, and check heap size
@@ -170,7 +170,7 @@ test_spawn_sub_binary() ->
%% Spawn a function, passing a refc binary through the args
%%
LargeSubBin = binary:part(Bin, 1, BinarySize - 1),
- Pid = erlang:spawn(?MODULE, loop, [#state{bin = LargeSubBin}]),
+ Pid = spawn_opt(?MODULE, loop, [#state{bin = LargeSubBin}], []),
PidHeapSize0 = get_heap_size(Pid),
%%
%% Make sure we can get what we spawned
@@ -195,7 +195,7 @@ test_spawn_fun_sub_binary() ->
%% Spawn a function, passing a refc binary through the args
%%
LargeSubBin = binary:part(Bin, 1, BinarySize - 1),
- Pid = erlang:spawn(fun() -> loop(#state{bin = LargeSubBin}) end),
+ Pid = spawn_opt(fun() -> loop(#state{bin = LargeSubBin}) end, []),
PidHeapSize0 = get_heap_size(Pid),
?VERIFY(MemoryBinarySize + 1024 == erlang:memory(binary)),
%%
@@ -338,7 +338,7 @@ create_string(N, Accum) ->
run_test(Fun) ->
Self = self(),
- _Pid = spawn(fun() -> execute(Self, Fun) end),
+ _Pid = spawn_opt(fun() -> execute(Self, Fun) end, []),
receive
ok ->
ok;
diff --git a/tests/erlang_tests/test_system_info.erl b/tests/erlang_tests/test_system_info.erl
index 834de5a32..69dee3f5c 100644
--- a/tests/erlang_tests/test_system_info.erl
+++ b/tests/erlang_tests/test_system_info.erl
@@ -80,7 +80,7 @@ test_process_count(_) ->
test_process_count() ->
Count = erlang:system_info(process_count),
Self = self(),
- Pid = spawn(?MODULE, loop, [Self]),
+ Pid = spawn_opt(?MODULE, loop, [Self], []),
receive
ok -> ok
end,
diff --git a/tests/erlang_tests/test_throw_call_ext_last.erl b/tests/erlang_tests/test_throw_call_ext_last.erl
index fadb361b9..7c37d0488 100644
--- a/tests/erlang_tests/test_throw_call_ext_last.erl
+++ b/tests/erlang_tests/test_throw_call_ext_last.erl
@@ -38,7 +38,7 @@ test_spawn_fun_sub_binary() ->
%% Spawn a function, passing a refc binary through the args
%%
LargeSubBin = binary:part(Bin, 1, BinarySize - 1),
- Pid = erlang:spawn(fun() -> loop(#state{bin = LargeSubBin}) end),
+ Pid = spawn_opt(fun() -> loop(#state{bin = LargeSubBin}) end, []),
PidHeapSize0 = get_heap_size(Pid),
%%
%% Make sure we can get what we spawned
@@ -113,7 +113,7 @@ create_string(N, Accum) ->
run_test(Fun) ->
Self = self(),
- _Pid = spawn(fun() -> execute(Self, Fun) end),
+ _Pid = spawn_opt(fun() -> execute(Self, Fun) end, []),
receive
ok ->
ok;
diff --git a/tests/erlang_tests/test_timeout_not_integer.erl b/tests/erlang_tests/test_timeout_not_integer.erl
index f2ca54863..24217377f 100644
--- a/tests/erlang_tests/test_timeout_not_integer.erl
+++ b/tests/erlang_tests/test_timeout_not_integer.erl
@@ -27,8 +27,8 @@ start() ->
test_infinity() ->
Parent = self(),
- _Pid1 = spawn(fun() -> waiter1(Parent) end),
- _Pid2 = spawn(fun() -> waiter2(Parent) end),
+ _Pid1 = spawn_opt(fun() -> waiter1(Parent) end, []),
+ _Pid2 = spawn_opt(fun() -> waiter2(Parent) end, []),
receive
_ -> 1
% wait 2 secs as with bug we waited few ms
diff --git a/tests/erlang_tests/trap_exit_flag.erl b/tests/erlang_tests/trap_exit_flag.erl
index 72d324445..64c5b3699 100644
--- a/tests/erlang_tests/trap_exit_flag.erl
+++ b/tests/erlang_tests/trap_exit_flag.erl
@@ -20,41 +20,60 @@
-module(trap_exit_flag).
--export([start/0, sum/1, proc/1]).
+-export([start/0]).
start() ->
- L = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
- Pid = spawn(?MODULE, proc, [L]),
false = erlang:process_flag(trap_exit, true),
+ ok = test_nocatch(),
+ ok = test_normal(),
+ true = erlang:process_flag(trap_exit, false),
+ ok = test_trap_exit_false(),
+ 0.
+
+test_nocatch() ->
+ Pid = spawn_opt(fun proc/0, []),
erlang:link(Pid),
- Pid ! {self(), sum},
- receive
- {'EXIT', Pid, {{nocatch, test}, EL}} when is_pid(Pid) andalso is_list(EL) ->
- 1;
- {'EXIT', _, normal} = T ->
- erlang:display(T),
- 2;
- {'EXIT', _, _} = T ->
- erlang:display(T),
- 3;
- T when is_tuple(T) ->
- erlang:display(T),
- 4;
- T ->
- erlang:display(T),
- 5
- after 30000 ->
- erlang:display(timeout),
- 6
- end.
+ Pid ! do_throw,
+ ok =
+ receive
+ {'EXIT', Pid, {{nocatch, test}, EL}} when is_list(EL) ->
+ ok;
+ Other ->
+ {unexpected, Other}
+ after 500 ->
+ timeout
+ end.
+
+test_normal() ->
+ Pid = spawn_opt(fun proc/0, []),
+ erlang:link(Pid),
+ Pid ! exit_normally,
+ ok =
+ receive
+ {'EXIT', Pid, normal} ->
+ ok;
+ Other ->
+ {unexpected, Other}
+ after 500 ->
+ timeout
+ end.
+
+test_trap_exit_false() ->
+ Pid = spawn_opt(fun proc/0, []),
+ erlang:link(Pid),
+ Pid ! exit_normally,
+ ok =
+ receive
+ Other ->
+ {unexpected, Other}
+ after 500 ->
+ ok
+ end.
-proc(_L) ->
+proc() ->
receive
- {_Pid, sum} ->
- throw(test)
+ do_throw ->
+ throw(test);
+ exit_normally ->
+ ok
end.
-
-sum([]) ->
- 0;
-sum([H | T]) ->
- H + sum(T).
diff --git a/tests/erlang_tests/unlink_error.erl b/tests/erlang_tests/unlink_error.erl
index fd24cc361..e502ad021 100644
--- a/tests/erlang_tests/unlink_error.erl
+++ b/tests/erlang_tests/unlink_error.erl
@@ -51,7 +51,7 @@ start() ->
start2() ->
L = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
- Pid = spawn(?MODULE, proc, [L]),
+ Pid = spawn_opt(?MODULE, proc, [L], []),
erlang:link(Pid),
erlang:unlink(Pid),
Pid ! {self(), sum},
diff --git a/tests/erlang_tests/whereis_dead_process.erl b/tests/erlang_tests/whereis_dead_process.erl
index 96fcf1bce..1ae149baa 100644
--- a/tests/erlang_tests/whereis_dead_process.erl
+++ b/tests/erlang_tests/whereis_dead_process.erl
@@ -34,7 +34,7 @@ test_two_names() ->
test_names([foo, bar]).
test_names(Names) ->
- Pid = spawn(fun() -> do_register(Names) end),
+ Pid = spawn_opt(fun() -> do_register(Names) end, []),
Monitor = monitor(process, Pid),
ok =
receive
diff --git a/tests/libs/estdlib/CMakeLists.txt b/tests/libs/estdlib/CMakeLists.txt
index 7fbf4428b..a3972b4e5 100644
--- a/tests/libs/estdlib/CMakeLists.txt
+++ b/tests/libs/estdlib/CMakeLists.txt
@@ -33,6 +33,7 @@ set(ERLANG_MODULES
test_lists
test_logger
test_maps
+ test_spawn
test_string
test_proplists
test_timer
diff --git a/tests/libs/estdlib/test_gen_server.erl b/tests/libs/estdlib/test_gen_server.erl
index 9ac933211..bf88d1b16 100644
--- a/tests/libs/estdlib/test_gen_server.erl
+++ b/tests/libs/estdlib/test_gen_server.erl
@@ -67,12 +67,15 @@ test_start_link() ->
pong = gen_server:call(Pid, ping),
pong = gen_server:call(Pid, reply_ping),
- erlang:process_flag(trap_exit, true),
+ false = erlang:process_flag(trap_exit, true),
ok = gen_server:cast(Pid, crash),
- receive
- {'EXIT', Pid, _Reason} -> ok
- after 30000 -> timeout
- end.
+ ok =
+ receive
+ {'EXIT', Pid, _Reason} -> ok
+ after 30000 -> timeout
+ end,
+ true = erlang:process_flag(trap_exit, false),
+ ok.
test_cast() ->
{ok, Pid} = gen_server:start(?MODULE, [], []),
diff --git a/tests/libs/estdlib/test_gen_statem.erl b/tests/libs/estdlib/test_gen_statem.erl
index 25fad2fa0..e7ddf361e 100644
--- a/tests/libs/estdlib/test_gen_statem.erl
+++ b/tests/libs/estdlib/test_gen_statem.erl
@@ -93,12 +93,15 @@ test_start_link() ->
pong = gen_statem:call(Pid, ping),
- erlang:process_flag(trap_exit, true),
+ false = erlang:process_flag(trap_exit, true),
ok = gen_statem:cast(Pid, crash),
- receive
- {'EXIT', Pid, _Reason} -> ok
- after 30000 -> timeout
- end.
+ ok =
+ receive
+ {'EXIT', Pid, _Reason} -> ok
+ after 30000 -> timeout
+ end,
+ true = erlang:process_flag(trap_exit, false),
+ ok.
%%
%% callbacks
diff --git a/tests/libs/estdlib/test_lists.erl b/tests/libs/estdlib/test_lists.erl
index e01f802d6..57b22b28f 100644
--- a/tests/libs/estdlib/test_lists.erl
+++ b/tests/libs/estdlib/test_lists.erl
@@ -42,6 +42,7 @@ test() ->
ok = test_join(),
ok = test_seq(),
ok = test_sort(),
+ ok = test_usort(),
ok.
test_nth() ->
@@ -208,10 +209,11 @@ test_sort() ->
?ASSERT_MATCH(lists:sort([]), []),
?ASSERT_MATCH(lists:sort([1]), [1]),
?ASSERT_MATCH(lists:sort([1, 3, 5, 2, 4]), [1, 2, 3, 4, 5]),
+ ?ASSERT_MATCH(lists:sort([1, 3, 5, 2, 3, 4]), [1, 2, 3, 3, 4, 5]),
?ASSERT_MATCH(lists:sort([c, a, b, d]), [a, b, c, d]),
?ASSERT_MATCH(lists:sort([#{}, z]), [z, #{}]),
- ?ASSERT_MATCH(lists:sort(fun(A, B) -> A > B end, [1, 2, 3, 4, 5]), [5, 4, 3, 2, 1]),
+ ?ASSERT_MATCH(lists:sort(fun(A, B) -> A > B end, [1, 2, 3, 4, 3, 5]), [5, 4, 3, 3, 2, 1]),
?ASSERT_FAILURE(lists:sort(1), function_clause),
?ASSERT_FAILURE(lists:sort(fun(A, B) -> A > B end, 1), function_clause),
@@ -219,4 +221,20 @@ test_sort() ->
ok.
+test_usort() ->
+ ?ASSERT_MATCH(lists:usort([]), []),
+ ?ASSERT_MATCH(lists:usort([1]), [1]),
+ ?ASSERT_MATCH(lists:usort([1, 3, 5, 2, 3, 4]), [1, 2, 3, 4, 5]),
+ ?ASSERT_MATCH(lists:usort([1, 3, 5, 2, 1, 4]), [1, 2, 3, 4, 5]),
+ ?ASSERT_MATCH(lists:usort([1, 3, 5, 2, 5, 4]), [1, 2, 3, 4, 5]),
+
+ ?ASSERT_MATCH(lists:usort(fun(A, B) -> A > B end, [1, 2, 3, 4, 3, 5]), [5, 4, 3, 3, 2, 1]),
+ ?ASSERT_MATCH(lists:usort(fun(A, B) -> A >= B end, [1, 2, 3, 4, 3, 5]), [5, 4, 3, 2, 1]),
+
+ ?ASSERT_FAILURE(lists:usort(1), function_clause),
+ ?ASSERT_FAILURE(lists:usort(fun(A, B) -> A > B end, 1), function_clause),
+ ?ASSERT_FAILURE(lists:usort(1, [1]), function_clause),
+
+ ok.
+
id(X) -> X.
diff --git a/tests/libs/estdlib/test_spawn.erl b/tests/libs/estdlib/test_spawn.erl
new file mode 100644
index 000000000..c1af92136
--- /dev/null
+++ b/tests/libs/estdlib/test_spawn.erl
@@ -0,0 +1,68 @@
+%
+% This file is part of AtomVM.
+%
+% Copyright 2023 Paul Guyot
+%
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+%
+% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
+%
+
+-module(test_spawn).
+
+-export([test/0, f/1]).
+
+-include("etest.hrl").
+
+test() ->
+ ok = test_spawn(),
+ ok = test_spawn_link(),
+ ok.
+
+%% spawn/1, spawn/3, spawn_link/1, spawn_link/3 are not nifs but implemented
+%% in erlang module.
+
+test_spawn() ->
+ Parent = self(),
+ Pid1 = spawn(fun() -> f(Parent) end),
+ ok =
+ receive
+ {Pid1, {links, []}} -> ok
+ after 500 -> timeout
+ end,
+ Pid2 = spawn(?MODULE, f, [Parent]),
+ ok =
+ receive
+ {Pid2, {links, []}} -> ok
+ after 500 -> timeout
+ end,
+ ok.
+
+test_spawn_link() ->
+ Parent = self(),
+ Pid1 = spawn_link(fun() -> f(Parent) end),
+ ok =
+ receive
+ {Pid1, {links, [Parent]}} -> ok
+ after 500 -> timeout
+ end,
+ Pid2 = spawn_link(?MODULE, f, [Parent]),
+ ok =
+ receive
+ {Pid2, {links, [Parent]}} -> ok
+ after 500 -> timeout
+ end,
+ ok.
+
+f(Parent) ->
+ Parent ! {self(), erlang:process_info(self(), links)}.
diff --git a/tests/libs/estdlib/tests.erl b/tests/libs/estdlib/tests.erl
index 84f7f4dc7..a5aaf8c8e 100644
--- a/tests/libs/estdlib/tests.erl
+++ b/tests/libs/estdlib/tests.erl
@@ -36,5 +36,6 @@ start() ->
test_maps,
test_proplists,
test_timer,
+ test_spawn,
test_supervisor
]).
diff --git a/tests/test.c b/tests/test.c
index b9941c668..ba4f93a1e 100644
--- a/tests/test.c
+++ b/tests/test.c
@@ -502,7 +502,9 @@ struct Test tests[] = {
TEST_CASE_EXPECTED(link_kill_parent, 1),
TEST_CASE_EXPECTED(link_throw, 1),
TEST_CASE_EXPECTED(unlink_error, 1),
- TEST_CASE_EXPECTED(trap_exit_flag, 1),
+ TEST_CASE(trap_exit_flag),
+ TEST_CASE(test_exit1),
+ TEST_CASE(test_exit2),
TEST_CASE_COND(test_stacktrace, 0, SKIP_STACKTRACES),
TEST_CASE(small_big_ext),
TEST_CASE(test_crypto),