From 73848187fd298c783dbdec0180426d4aee6f5cbf Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Fri, 5 Dec 2025 17:50:59 +0100 Subject: [PATCH 01/23] Process aliases Signed-off-by: Mateusz Front --- libs/estdlib/src/erlang.erl | 54 ++++++- src/libAtomVM/context.c | 68 ++++++++- src/libAtomVM/context.h | 35 +++++ src/libAtomVM/defaultatoms.def | 6 + src/libAtomVM/external_term.c | 15 ++ src/libAtomVM/jit.c | 42 +++++- src/libAtomVM/nifs.c | 163 +++++++++++++++++++-- src/libAtomVM/nifs.gperf | 3 + src/libAtomVM/opcodesswitch.h | 24 ++- src/libAtomVM/term.c | 6 + src/libAtomVM/term.h | 68 +++++++++ tests/erlang_tests/test_binary_to_term.erl | 30 ++-- tests/erlang_tests/test_monitor.erl | 151 +++++++++++++++++++ 13 files changed, 627 insertions(+), 38 deletions(-) diff --git a/libs/estdlib/src/erlang.erl b/libs/estdlib/src/erlang.erl index e7a9b35468..ca1e985381 100644 --- a/libs/estdlib/src/erlang.erl +++ b/libs/estdlib/src/erlang.erl @@ -100,6 +100,7 @@ make_ref/0, send/2, monitor/2, + monitor/3, demonitor/1, demonitor/2, exit/1, @@ -135,7 +136,9 @@ dist_ctrl_put_data/2, unique_integer/0, unique_integer/1, - raise/3 + raise/3, + alias/0, + unalias/1 ]). -export_type([ @@ -179,7 +182,8 @@ | {max_heap_size, pos_integer()} | {atomvm_heap_growth, atomvm_heap_growth_strategy()} | link - | monitor. + | monitor + | {monitor, [monitor_option()]}. -type send_destination() :: pid() @@ -205,6 +209,8 @@ -type raise_stacktrace() :: [{module(), atom(), arity() | [term()]} | {function(), arity() | [term()]}] | stacktrace(). +-type monitor_option() :: {'alias', 'explicit_unalias' | 'demonitor' | 'reply_demonitor'}. + %%----------------------------------------------------------------------------- %% @param Time time in milliseconds after which to send the timeout message. %% @param Dest Pid or server name to which to send the timeout message. @@ -1173,6 +1179,28 @@ send(_Target, _Message) -> monitor(_Type, _PidOrPort) -> erlang:nif_error(undefined). +%%----------------------------------------------------------------------------- +%% @param Type type of monitor to create +%% @param PidOrPort pid or port of the object to monitor +%% @param Options monitor options +%% @returns a monitor reference +%% @doc Creates a monitor and allows passing additional options. +%% Currently, only the `{alias, AliasMode}' option is supported. Passing it +%% makes the monitor also an alias on the calling process (see `alias/0'). +%% `AliasMode' defines the behaviour of the alias: +%% - explicit_unalias - the alias can be only removed with `unalias/1', +%% - demonitor - the alias is also removed when `demonitor/1' is called +%% on the monitor, +%% - reply_demonitor - the alias is also removed after a first message +%% is sent via it. +%% @end +%%----------------------------------------------------------------------------- +-spec monitor + (Type :: process, Pid :: pid() | atom(), [monitor_option()]) -> reference(); + (Type :: port, Port :: port() | atom(), [monitor_option()]) -> reference(). +monitor(_Type, _PidOrPort, _Options) -> + erlang:nif_error(undefined). + %%----------------------------------------------------------------------------- %% @param Monitor reference of monitor to remove %% @returns `true' @@ -1593,3 +1621,25 @@ nif_error(_Reason) -> no_return(). raise(_Class, _Reason, _Stacktrace) -> erlang:nif_error(undefined). + +%%----------------------------------------------------------------------------- +%% @returns A reference aliasing the calling process. +%% @doc Creates an alias for the callling process. The alias can be used +%% to send messages to the process like the PID. The alias can also be +%% created along with a monitor - see `monitor/3'. The alias can be +%% removed by calling `unalias/1'. +%% @end +%%----------------------------------------------------------------------------- +-spec alias() -> Alias when Alias :: reference(). +alias() -> + erlang:nif_error(undefined). + +%%----------------------------------------------------------------------------- +%% @param Alias the alias to be removed. +%% @returns `true' if alias was removed, `false' if it was not found +%% @doc Removes process alias. See `alias/0' for more information. +%% @end +%%----------------------------------------------------------------------------- +-spec unalias(Alias) -> boolean() when Alias :: reference(). +unalias(_Alias) -> + erlang:nif_error(undefined). diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index 0e35082ad1..afb58b0ea9 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -264,6 +264,7 @@ void context_destroy(Context *ctx) case CONTEXT_MONITOR_MONITORED_LOCAL: case CONTEXT_MONITOR_MONITORING_LOCAL: case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: + case CONTEXT_MONITOR_ALIAS: UNREACHABLE(); } } @@ -778,7 +779,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) Context *target = globalcontext_get_process_nolock(glb, local_process_id); // Target cannot be NULL as we processed Demonitor signals assert(target != NULL); - int required_terms = REF_SIZE + TUPLE_SIZE(5); + int required_terms = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(5); if (UNLIKELY(memory_ensure_free(ctx, required_terms) != MEMORY_GC_OK)) { // TODO: handle out of memory here fprintf(stderr, "Cannot handle out of memory.\n"); @@ -786,7 +787,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) AVM_ABORT(); } // Prepare the message on ctx's heap which will be freed afterwards. - term ref = term_from_ref_ticks(monitored_monitor->ref_ticks, &ctx->heap); + term ref = term_make_process_reference(target->process_id, monitored_monitor->ref_ticks, &ctx->heap); term port_or_process = term_pid_or_port_from_context(ctx); term port_or_process_atom @@ -803,6 +804,9 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) free(monitored_monitor); break; } + case CONTEXT_MONITOR_ALIAS: { + free(monitor); + } } } return result; @@ -873,6 +877,19 @@ struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, t return &monitor->monitor; } +struct Monitor *monitor_alias_new(uint64_t ref_ticks, enum ContextMonitorAliasType alias_type) +{ + struct MonitorAlias *monitor = malloc(sizeof(struct MonitorAlias)); + if (IS_NULL_PTR(monitor)) { + return NULL; + } + monitor->monitor.monitor_type = CONTEXT_MONITOR_ALIAS; + monitor->ref_ticks = ref_ticks; + monitor->alias_type = alias_type; + + return &monitor->monitor; +} + struct Monitor *monitor_resource_monitor_new(void *resource, uint64_t ref_ticks) { struct ResourceContextMonitor *monitor = malloc(sizeof(struct ResourceContextMonitor)); @@ -924,6 +941,16 @@ bool context_add_monitor(Context *ctx, struct Monitor *new_monitor) } break; } + case CONTEXT_MONITOR_ALIAS: { + struct MonitorAlias *new_alias_monitor = CONTAINER_OF(new_monitor, struct MonitorAlias, monitor); + struct MonitorAlias *existing_alias_monitor = CONTAINER_OF(existing, struct MonitorAlias, monitor); + + if (UNLIKELY(existing_alias_monitor->alias_type == new_alias_monitor->alias_type && existing_alias_monitor->ref_ticks == new_alias_monitor->ref_ticks)) { + free(new_monitor); + return false; + } + break; + } case CONTEXT_MONITOR_RESOURCE: { struct ResourceContextMonitor *new_resource_monitor = CONTAINER_OF(new_monitor, struct ResourceContextMonitor, monitor); struct ResourceContextMonitor *existing_resource_monitor = CONTAINER_OF(existing, struct ResourceContextMonitor, monitor); @@ -1057,6 +1084,11 @@ void context_unlink_ack(Context *ctx, term link_pid, uint64_t unlink_id) void context_demonitor(Context *ctx, uint64_t ref_ticks) { + struct MonitorAlias *alias = context_find_alias(ctx, ref_ticks); + if (alias != NULL && alias->alias_type != ContextMonitorAliasExplicitUnalias) { + context_unalias(alias); + } + struct ListHead *item; LIST_FOR_EACH (item, &ctx->monitors_head) { struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head); @@ -1090,11 +1122,36 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks) } case CONTEXT_MONITOR_LINK_LOCAL: case CONTEXT_MONITOR_LINK_REMOTE: + case CONTEXT_MONITOR_ALIAS: break; } } } +struct MonitorAlias *context_find_alias(Context *ctx, uint64_t ref_ticks) +{ + struct ListHead *item; + LIST_FOR_EACH (item, &ctx->monitors_head) { + struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head); + if (monitor->monitor_type == CONTEXT_MONITOR_ALIAS) { + struct MonitorAlias *alias_monitor = CONTAINER_OF(monitor, struct MonitorAlias, monitor); + if (alias_monitor->ref_ticks == ref_ticks) { + return alias_monitor; + } + } + } + + return NULL; +} + +void context_unalias(struct MonitorAlias *alias) +{ + TERM_DEBUG_ASSERT(alias != NULL); + struct Monitor *monitor = &alias->monitor; + list_remove(&monitor->monitor_list_head); + free(monitor); +} + term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitoring) { struct ListHead *item; @@ -1121,6 +1178,7 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori case CONTEXT_MONITOR_LINK_LOCAL: case CONTEXT_MONITOR_LINK_REMOTE: case CONTEXT_MONITOR_RESOURCE: + case CONTEXT_MONITOR_ALIAS: break; } } @@ -1250,6 +1308,12 @@ COLD_FUNC void context_dump(Context *ctx) fprintf(stderr, "\n"); break; } + case CONTEXT_MONITOR_ALIAS: { + struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); + fprintf(stderr, "has alias ref=%lu", (long unsigned) monitored_monitor->ref_ticks); + fprintf(stderr, "\n"); + break; + } case CONTEXT_MONITOR_MONITORED_LOCAL: { struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); fprintf(stderr, "monitored by "); diff --git a/src/libAtomVM/context.h b/src/libAtomVM/context.h index 52ac76d3bd..b33b1d70c3 100644 --- a/src/libAtomVM/context.h +++ b/src/libAtomVM/context.h @@ -177,6 +177,14 @@ enum ContextMonitorType CONTEXT_MONITOR_RESOURCE, CONTEXT_MONITOR_LINK_REMOTE, CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME, + CONTEXT_MONITOR_ALIAS, +}; + +enum ContextMonitorAliasType +{ + ContextMonitorAliasExplicitUnalias, + ContextMonitorAliasDemonitor, + ContextMonitorAliasReplyDemonitor, }; #define UNLINK_ID_LINK_ACTIVE 0x0 @@ -212,6 +220,13 @@ struct MonitorLocalRegisteredNameMonitor term monitor_name; }; +struct MonitorAlias +{ + struct Monitor monitor; + uint64_t ref_ticks; + enum ContextMonitorAliasType alias_type; +}; + // The other half is called ResourceMonitor and is a linked list of resources struct ResourceContextMonitor { @@ -517,6 +532,8 @@ struct Monitor *monitor_link_new(term link_pid); */ struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monitoring); +struct Monitor *monitor_alias_new(uint64_t ref_ticks, enum ContextMonitorAliasType alias_type); + /** * @brief Create a monitor on a process by registered name. * @@ -582,6 +599,24 @@ void context_unlink_ack(Context *ctx, term link_pid, uint64_t unlink_id); */ void context_demonitor(Context *ctx, uint64_t ref_ticks); +/** + * @brief Find a process alias + * @details Called within the process only + * + * @param ctx the context being executed + * @param ref_ticks reference of the alias to remove + * @return found alias or NULL + */ +struct MonitorAlias *context_find_alias(Context *ctx, uint64_t ref_ticks); + +/** + * @brief Remove an alias of a process + * @details Called within the process only + * + * @param alias The alias to remove, can be obtained using context_find_alias + */ +void context_unalias(struct MonitorAlias *alias); + /** * @brief Get target of a monitor. * diff --git a/src/libAtomVM/defaultatoms.def b/src/libAtomVM/defaultatoms.def index 03236eb45e..859048b0ff 100644 --- a/src/libAtomVM/defaultatoms.def +++ b/src/libAtomVM/defaultatoms.def @@ -212,3 +212,9 @@ X(JIT_X86_64_ATOM, "\xA", "jit_x86_64") X(JIT_AARCH64_ATOM, "\xB", "jit_aarch64") X(JIT_ARMV6M_ATOM, "\xA", "jit_armv6m") X(JIT_RISCV32_ATOM, "\xB", "jit_riscv32") + +X(ALIAS_ATOM, "\x5", "alias") +X(DEMONITOR_ATOM, "\x9", "demonitor") +X(EXPLICIT_UNALIAS_ATOM, "\x10", "explicit_unalias") +X(REPLY_DEMONITOR_ATOM, "\xF", "reply_demonitor") +X(TAG_ATOM, "\x3", "tag") diff --git a/src/libAtomVM/external_term.c b/src/libAtomVM/external_term.c index 7720d3c290..ed9e2de3d4 100644 --- a/src/libAtomVM/external_term.c +++ b/src/libAtomVM/external_term.c @@ -522,6 +522,8 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb) uint32_t len; if (term_is_resource_reference(t)) { len = 4; + } else if (term_is_process_reference(t)) { + len = 3; } else { len = 2; } @@ -543,6 +545,15 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb) WRITE_64_UNALIGNED(buf + k + 12, ((uintptr_t) serialize_ref)); } return k + 20; + } else if (term_is_process_reference(t)) { + if (!IS_NULL_PTR(buf)) { + uint64_t ticks = term_to_ref_ticks(t); + uint32_t process_id = term_process_ref_to_process_id(t); + WRITE_32_UNALIGNED(buf + k, creation); + WRITE_64_UNALIGNED(buf + k + 4, ticks); + WRITE_32_UNALIGNED(buf + k + 12, process_id); + } + return k + 16; } else { if (!IS_NULL_PTR(buf)) { uint64_t ticks = term_to_ref_ticks(t); @@ -931,6 +942,10 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm if (len == 2 && node == this_node && creation == this_creation) { uint64_t ticks = ((uint64_t) data[0]) << 32 | data[1]; return term_from_ref_ticks(ticks, heap); + } else if (len == 3 && node == this_node && creation == this_creation) { + uint64_t ticks = ((uint64_t) data[0]) << 32 | data[1]; + uint32_t process_id = data[2]; + return term_make_process_reference(process_id, ticks, heap); } else if (len == 4 && node == this_node && creation == this_creation) { // This is a resource uint64_t resource_type_ptr = ((uint64_t) data[0]) << 32 | data[1]; diff --git a/src/libAtomVM/jit.c b/src/libAtomVM/jit.c index 21feac6264..386871d981 100644 --- a/src/libAtomVM/jit.c +++ b/src/libAtomVM/jit.c @@ -758,15 +758,22 @@ static bool jit_send(Context *ctx, JITState *jit_state) return false; } ctx->x[0] = return_value; - } else { - if (term_is_atom(recipient_term)) { - recipient_term = globalcontext_get_registered_process(ctx->global, term_to_atom_index(recipient_term)); - if (UNLIKELY(recipient_term == UNDEFINED_ATOM)) { - set_error(ctx, jit_state, 0, BADARG_ATOM); - return false; - } + } else if (term_is_local_pid_or_port(recipient_term)) { + int local_process_id; + if (term_is_local_pid_or_port(recipient_term)) { + local_process_id = term_to_local_process_id(recipient_term); + } else { + set_error(ctx, jit_state, 0, BADARG_ATOM); + return false; + } + globalcontext_send_message(ctx->global, local_process_id, ctx->x[1]); + ctx->x[0] = ctx->x[1]; + } else if (term_is_atom(recipient_term)) { + recipient_term = globalcontext_get_registered_process(ctx->global, term_to_atom_index(recipient_term)); + if (UNLIKELY(recipient_term == UNDEFINED_ATOM)) { + set_error(ctx, jit_state, 0, BADARG_ATOM); + return false; } - int local_process_id; if (term_is_local_pid_or_port(recipient_term)) { local_process_id = term_to_local_process_id(recipient_term); @@ -776,7 +783,26 @@ static bool jit_send(Context *ctx, JITState *jit_state) } globalcontext_send_message(ctx->global, local_process_id, ctx->x[1]); ctx->x[0] = ctx->x[1]; + } else if (term_is_process_reference(recipient_term)) { + int32_t process_id = term_process_ref_to_process_id(recipient_term); + int64_t ref_ticks = term_to_ref_ticks(recipient_term); + Context *p = globalcontext_get_process_lock(ctx->global, process_id); + if (p) { + struct MonitorAlias *alias = context_find_alias(p, ref_ticks); + if (!IS_NULL_PTR(alias)) { + if (alias->alias_type == ContextMonitorAliasReplyDemonitor) { + context_unalias(alias); + } + mailbox_send(p, ctx->x[1]); + } + globalcontext_get_process_unlock(ctx->global, p); + } + ctx->x[0] = ctx->x[1]; + } else if (!term_is_reference(recipient_term)) { + set_error(ctx, jit_state, 0, BADARG_ATOM); + return false; } + return true; } diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 20f2eea4f7..8f559f6d1f 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -280,6 +280,8 @@ static term nif_maps_next(Context *ctx, int argc, term argv[]); static term nif_unicode_characters_to_list(Context *ctx, int argc, term argv[]); static term nif_unicode_characters_to_binary(Context *ctx, int argc, term argv[]); static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[]); +static term nif_erlang_alias(Context *ctx, int argc, term argv[]); +static term nif_erlang_unalias(Context *ctx, int argc, term argv[]); static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]); #define DECLARE_MATH_NIF_FUN(moniker) \ @@ -910,6 +912,14 @@ static const struct Nif list_to_bitstring_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_erlang_list_to_bitstring_1 }; +static const struct Nif erlang_alias_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_erlang_alias +}; +static const struct Nif erlang_unalias_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_erlang_unalias +}; static const struct Nif zlib_compress_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_zlib_compress_1 @@ -1285,6 +1295,42 @@ static NativeHandlerResult process_console_mailbox(Context *ctx) return result; } +static term parse_monitor_opts(Context *ctx, term monitor_opts, bool *is_alias, enum ContextMonitorAliasType *alias_type) +{ + *is_alias = false; + while (term_is_nonempty_list(monitor_opts)) { + term option = term_get_list_head(monitor_opts); + if (term_is_tuple(option) && term_get_tuple_element(option, 0) == ALIAS_ATOM) { + *is_alias = true; + switch (term_get_tuple_element(option, 1)) { + case EXPLICIT_UNALIAS_ATOM: + *alias_type = ContextMonitorAliasExplicitUnalias; + break; + case DEMONITOR_ATOM: + *alias_type = ContextMonitorAliasDemonitor; + break; + case REPLY_DEMONITOR_ATOM: + *alias_type = ContextMonitorAliasReplyDemonitor; + break; + default: + RAISE_ERROR(BADARG_ATOM); + } + } else if (term_is_tuple(option) && term_get_tuple_element(option, 0) == TAG_ATOM) { + RAISE_ERROR(UNSUPPORTED_ATOM); + } else { + RAISE_ERROR(BADARG_ATOM); + } + + monitor_opts = term_get_list_tail(monitor_opts); + } + + if (UNLIKELY(!term_is_nil(monitor_opts))) { + return RAISE_ERROR(BADARG_ATOM); + } + + return OK_ATOM; +} + // 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, size_t arity, size_t n_freeze, term opts_term) @@ -1292,7 +1338,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free 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); + term monitor_term = interop_proplist_get_value_default(opts_term, MONITOR_ATOM, term_invalid_term()); term heap_growth_strategy = interop_proplist_get_value_default(opts_term, ATOMVM_HEAP_GROWTH_ATOM, BOUNDED_FREE_ATOM); term request_term = interop_proplist_get_value_default(opts_term, REQUEST_ATOM, UNDEFINED_ATOM); term group_leader; @@ -1382,6 +1428,16 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free context_add_monitor(ctx, self_link); } if (monitor_term == TRUE_ATOM) { + monitor_term = term_nil(); + } + if (term_is_list(monitor_term)) { + bool is_alias; + enum ContextMonitorAliasType alias_type; + + if (UNLIKELY(term_is_invalid_term(parse_monitor_opts(ctx, monitor_term, &is_alias, &alias_type)))) { + context_destroy(new_ctx); + return term_invalid_term(); + } // We can call context_add_monitor directly on new process because it's not started yet ref_ticks = globalcontext_get_ref_ticks(ctx->global); struct Monitor *new_monitor = monitor_new(term_from_local_process_id(ctx->process_id), ref_ticks, false); @@ -1395,12 +1451,25 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free context_destroy(new_ctx); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } + struct Monitor *alias_monitor = NULL; + if (is_alias) { + alias_monitor = monitor_alias_new(ref_ticks, alias_type); + if (IS_NULL_PTR(alias_monitor)) { + free(new_monitor); + free(self_monitor); + context_destroy(new_ctx); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + } context_add_monitor(new_ctx, new_monitor); context_add_monitor(ctx, self_monitor); + if (is_alias) { + context_add_monitor(ctx, alias_monitor); + } } if (ref_ticks) { - int res_size = REF_SIZE + TUPLE_SIZE(2); + int res_size = TERM_BOXED_PROCESS_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); @@ -1408,13 +1477,13 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free scheduler_init_ready(new_ctx); - term ref = term_from_ref_ticks(ref_ticks, &ctx->heap); + term ref = term_make_process_reference(ctx->process_id, 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); + term process_ref_tuple = term_alloc_tuple(2, &ctx->heap); + term_put_tuple_element(process_ref_tuple, 0, new_pid); + term_put_tuple_element(process_ref_tuple, 1, ref); - return pid_ref_tuple; + return process_ref_tuple; } else if (UNLIKELY(valid_request)) { // Handling of spawn_request // spawn_request requires that the reply is enqueued before @@ -1597,6 +1666,20 @@ static term nif_erlang_send_2(Context *ctx, int argc, term argv[]) globalcontext_send_message(glb, local_process_id, argv[1]); + } else if (term_is_process_reference(target)) { + int32_t process_id = term_process_ref_to_process_id(target); + int64_t ref_ticks = term_to_ref_ticks(target); + Context *p = globalcontext_get_process_lock(glb, process_id); + if (p) { + struct MonitorAlias *alias = context_find_alias(p, ref_ticks); + if (alias != NULL) { + if (alias->alias_type == ContextMonitorAliasReplyDemonitor) { + context_unalias(alias); + } + mailbox_send(p, argv[1]); + } + globalcontext_get_process_unlock(glb, p); + } } else if (term_is_atom(target)) { // We need to hold a lock on the processes_table until the message is sent to avoid a race condition, // otherwise the receiving process could be killed at any point between checking it is registered, @@ -1621,7 +1704,7 @@ static term nif_erlang_send_2(Context *ctx, int argc, term argv[]) globalcontext_send_message_nolock(glb, local_process_id, argv[1]); synclist_unlock(&glb->processes_table); - } else { + } else if (!term_is_reference(target)) { RAISE_ERROR(BADARG_ATOM); } @@ -4314,17 +4397,22 @@ static term nif_erlang_memory(Context *ctx, int argc, term argv[]) static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) { - UNUSED(argc); term object_type = argv[0]; term target_proc = argv[1]; + term options = argc == 3 ? argv[2] : term_nil(); term target_pid; size_t target_proc_size = 0; + bool is_alias; + enum ContextMonitorAliasType alias_type; if (object_type != PROCESS_ATOM && object_type != PORT_ATOM) { RAISE_ERROR(BADARG_ATOM); } + if (UNLIKELY(term_is_invalid_term(parse_monitor_opts(ctx, options, &is_alias, &alias_type)))) { + return term_invalid_term(); + } if (term_is_atom(target_proc)) { target_pid = globalcontext_get_registered_process(ctx->global, term_to_atom_index(target_proc)); target_proc_size = TUPLE_SIZE(2); @@ -4357,12 +4445,12 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) } if (IS_NULL_PTR(target)) { - int res_size = REF_SIZE + TUPLE_SIZE(5) + target_proc_size; + int res_size = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(5) + target_proc_size; if (UNLIKELY(memory_ensure_free_opt(ctx, res_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global); - term ref = term_from_ref_ticks(ref_ticks, &ctx->heap); + term ref = term_make_process_reference(ctx->process_id, ref_ticks, &ctx->heap); term down_message_tuple = term_alloc_tuple(5, &ctx->heap); term_put_tuple_element(down_message_tuple, 0, DOWN_ATOM); term_put_tuple_element(down_message_tuple, 1, ref); @@ -4401,16 +4489,29 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) globalcontext_get_process_unlock(ctx->global, target); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } + struct Monitor *alias_monitor = NULL; + if (is_alias) { + alias_monitor = monitor_alias_new(ref_ticks, alias_type); + if (IS_NULL_PTR(alias_monitor)) { + free(self_monitor); + free(other_monitor); + globalcontext_get_process_unlock(ctx->global, target); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + } mailbox_send_monitor_signal(target, MonitorSignal, other_monitor); globalcontext_get_process_unlock(ctx->global, target); context_add_monitor(ctx, self_monitor); + if (is_alias) { + context_add_monitor(ctx, alias_monitor); + } - if (UNLIKELY(memory_ensure_free_opt(ctx, REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_PROCESS_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - return term_from_ref_ticks(ref_ticks, &ctx->heap); + return term_make_process_reference(ctx->process_id, ref_ticks, &ctx->heap); } static term nif_erlang_demonitor(Context *ctx, int argc, term argv[]) @@ -6568,6 +6669,42 @@ static term nif_erlang_list_to_bitstring_1(Context *ctx, int argc, term argv[]) return nif_erlang_list_to_binary_1(ctx, argc, argv); } +static term nif_erlang_alias(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_PROCESS_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global); + term process_ref = term_make_process_reference(ctx->process_id, ref_ticks, &ctx->heap); + struct Monitor *monitor = monitor_alias_new(ref_ticks, ContextMonitorAliasExplicitUnalias); + if (IS_NULL_PTR(monitor)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + context_add_monitor(ctx, monitor); + return process_ref; +} + +static term nif_erlang_unalias(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + term process_ref = argv[0]; + + VALIDATE_VALUE(process_ref, term_is_local_reference); + uint64_t ref_ticks = term_to_ref_ticks(process_ref); + struct MonitorAlias *alias = context_find_alias(ctx, ref_ticks); + if (IS_NULL_PTR(alias)) { + return FALSE_ATOM; + } else { + context_unalias(alias); + return TRUE_ATOM; + } +} + #ifdef WITH_ZLIB static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]) { diff --git a/src/libAtomVM/nifs.gperf b/src/libAtomVM/nifs.gperf index 9c87c362f9..0548761cea 100644 --- a/src/libAtomVM/nifs.gperf +++ b/src/libAtomVM/nifs.gperf @@ -89,6 +89,7 @@ erlang:make_ref/0, &make_ref_nif erlang:make_tuple/2, &make_tuple_nif erlang:memory/1, &memory_nif erlang:monitor/2, &monitor_nif +erlang:monitor/3, &monitor_nif erlang:demonitor/1, &demonitor_nif erlang:demonitor/2, &demonitor_nif erlang:is_process_alive/1, &is_process_alive_nif @@ -145,6 +146,8 @@ erlang:dist_ctrl_put_data/2, &dist_ctrl_put_data_nif erlang:module_loaded/1,&module_loaded_nif erlang:nif_error/1,&nif_error_nif erlang:list_to_bitstring/1, &list_to_bitstring_nif +erlang:alias/0, &erlang_alias_nif +erlang:unalias/1, &erlang_unalias_nif erts_debug:flat_size/1, &flat_size_nif ets:new/2, &ets_new_nif ets:insert/2, &ets_insert_nif diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index ab78fb8392..04f4ec7f55 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -2825,12 +2825,28 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) int local_process_id; if (term_is_local_pid_or_port(recipient_term)) { local_process_id = term_to_local_process_id(recipient_term); - } else { + TRACE("send/0 target_pid=%i\n", local_process_id); + TRACE_SEND(ctx, x_regs[0], x_regs[1]); + globalcontext_send_message(ctx->global, local_process_id, x_regs[1]); + } else if (term_is_process_reference(recipient_term)) { + int32_t local_process_id = term_process_ref_to_process_id(recipient_term); + TRACE("send/0 target_pid=%i\n", local_process_id); + TRACE_SEND(ctx, x_regs[0], x_regs[1]); + int64_t ref_ticks = term_to_ref_ticks(recipient_term); + Context *p = globalcontext_get_process_lock(glb, local_process_id); + if (p) { + struct MonitorAlias *alias = context_find_alias(p, ref_ticks); + if (alias != NULL) { + if (alias->alias_type == ContextMonitorAliasReplyDemonitor) { + context_unalias(alias); + } + mailbox_send(p, x_regs[1]); + } + globalcontext_get_process_unlock(glb, p); + } + } else if (!term_is_reference(recipient_term)) { RAISE_ERROR(BADARG_ATOM); } - TRACE("send/0 target_pid=%i\n", local_process_id); - TRACE_SEND(ctx, x_regs[0], x_regs[1]); - globalcontext_send_message(ctx->global, local_process_id, x_regs[1]); x_regs[0] = x_regs[1]; } #endif diff --git a/src/libAtomVM/term.c b/src/libAtomVM/term.c index eb627dd018..d8916e588f 100644 --- a/src/libAtomVM/term.c +++ b/src/libAtomVM/term.c @@ -405,6 +405,12 @@ int term_funprint(PrinterFun *fun, term t, const GlobalContext *global) uint64_t resource_ptr = (uintptr_t) refc_binary->data; return fun->print(fun, "#Ref<0.%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ">", (uint32_t) (resource_type_ptr >> 32), (uint32_t) resource_type_ptr, (uint32_t) (resource_ptr >> 32), (uint32_t) resource_ptr); + } else if (term_is_process_reference(t)) { + int32_t process_id = term_process_ref_to_process_id(t); + uint64_t ref_ticks = term_to_ref_ticks(t); + + // Update also REF_AS_CSTRING_LEN when changing this format string + return fun->print(fun, "#Ref<%" PRId32 ".%" PRIu32 ".%" PRIu32 ">", process_id, (uint32_t) (ref_ticks >> 32), (uint32_t) ref_ticks); } else if (term_is_local_reference(t)) { // Update also REF_AS_CSTRING_LEN when changing this format string uint64_t ref_ticks = term_to_ref_ticks(t); diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index eae9f58635..c8dbc17aef 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -152,6 +152,8 @@ extern "C" { #define BOXED_FUN_SIZE 3 #define FLOAT_SIZE (sizeof(float_term_t) / sizeof(term) + 1) #define REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) +#define TERM_BOXED_PROCESS_REF_SIZE (REF_SIZE + 1) +#define TERM_BOXED_PROCESS_REF_HEADER (((TERM_BOXED_PROCESS_REF_SIZE - 1) << 6) | TERM_BOXED_REF) #if TERM_BYTES == 8 #define EXTERNAL_PID_SIZE 3 #elif TERM_BYTES == 4 @@ -210,6 +212,9 @@ extern "C" { // Local ref is at most 30 bytes: // 2^32-1 = 4294967295 (10 chars) // "#Ref<0." "." ">\0" (10 chars) +// Process ref is at most 39 bytes: +// 2^32-1 = 4294967295 (10 chars) +// "#Ref<" "." "." ">\0" (9 chars) // Resource ref is at most 52 bytes: // 2^32-1 = 4294967295 (10 chars) // "#Ref<0." "." "." "." ">\0" (12 chars) @@ -877,6 +882,25 @@ static inline bool term_is_local_reference(term t) return false; } +/** + * @brief Checks if a term is a process reference + * + * @details See \c term_make_process_reference(). + * @param t the term that will be checked. + * @return \c true if check succeeds, \c false otherwise. + */ +static inline bool term_is_process_reference(term t) +{ + if (term_is_boxed(t)) { + const term *boxed_value = term_to_const_term_ptr(t); + if (boxed_value[0] == TERM_BOXED_PROCESS_REF_HEADER) { + return true; + } + } + + return false; +} + /** * @brief Checks if a term is an external reference * @@ -2190,6 +2214,50 @@ static inline uint64_t term_to_ref_ticks(term rt) #endif } +/** + * @brief Creates a process reference + * @details Process reference contains ref_ticks and process_id of a process. + * They are used by process aliases and monitors. + * + * @param process_id process_id of a process that the reference will identify. + * @param ref_ticks an unique uint64 value that will be used to create ref term. + * @param heap the heap to allocate memory in + * @return a ref term created using given ref ticks. + */ +static inline term term_make_process_reference(int32_t process_id, uint64_t ref_ticks, Heap *heap) +{ + term *boxed_value = memory_heap_alloc(heap, TERM_BOXED_PROCESS_REF_SIZE); + boxed_value[0] = TERM_BOXED_PROCESS_REF_HEADER; + +#if TERM_BYTES == 4 + boxed_value[1] = (ref_ticks >> 4); + boxed_value[2] = (ref_ticks & 0xFFFFFFFF); + boxed_value[3] = process_id; + +#elif TERM_BYTES == 8 + boxed_value[1] = (term) ref_ticks; + boxed_value[2] = process_id; + +#else +#error "terms must be either 32 or 64 bit wide" +#endif + + return ((term) boxed_value) | TERM_PRIMARY_BOXED; +} + +static inline uint32_t term_process_ref_to_process_id(term rt) +{ + TERM_DEBUG_ASSERT(term_is_process_reference(rt)); + const term *boxed_value = term_to_const_term_ptr(rt); +#if TERM_BYTES == 4 + return (uint32_t) boxed_value[3]; +#elif TERM_BYTES == 8 + return (uint32_t) boxed_value[2]; +#else +#error "terms must be either 32 or 64 bit wide" +#endif +} + /** * @brief Make an external pid term from node, process_id, serial and creation * diff --git a/tests/erlang_tests/test_binary_to_term.erl b/tests/erlang_tests/test_binary_to_term.erl index eb6129dc74..616479ad7c 100644 --- a/tests/erlang_tests/test_binary_to_term.erl +++ b/tests/erlang_tests/test_binary_to_term.erl @@ -180,6 +180,7 @@ start() -> ok = test_encode_port(), ok = test_atom_encoding(), ok = test_encode_resource(), + ok = test_encode_process_ref(), 0. test_reverse(T, Interop) -> @@ -1136,23 +1137,34 @@ test_encode_resource(OTPVersion) -> AlteredResource4 = binary_to_term(AlteredResourceBin4), false = AlteredResource4 =:= Resource, ok. +test_encode_process_ref() -> + AliasesAvailable = is_atomvm_or_otp_version_at_least("24"), + if + AliasesAvailable -> + ProcessRef = erlang:alias(), + ProcessRef = binary_to_term(term_to_binary(ProcessRef)), + ok; + true -> + ok + end. % Some binaries are re-encoded differently on earlier BEAM. Verify % term_to_binary(binary_to_term(Bin)) is idempotent on AtomVM and recent OTPs. binary_to_term_idempotent(Binary, OTPVersion) -> Term = binary_to_term(Binary), - case erlang:system_info(machine) of - "ATOM" -> - Binary = term_to_binary(Term); - "BEAM" -> - OTPRelease = erlang:system_info(otp_release), - if - OTPRelease >= OTPVersion -> Binary = term_to_binary(Term); - true -> ok - end + CanCheck = is_atomvm_or_otp_version_at_least(OTPVersion), + if + CanCheck -> Binary = term_to_binary(Term); + true -> ok end, Term. +is_atomvm_or_otp_version_at_least(OTPVersion) -> + case erlang:system_info(machine) of + "ATOM" -> true; + "BEAM" -> erlang:system_info(otp_release) >= OTPVersion + end. + test_atom_encoding() -> true = compare_pair_encoding(latin1_as_utf8_1), true = compare_pair_encoding(latin1_as_utf8_2), diff --git a/tests/erlang_tests/test_monitor.erl b/tests/erlang_tests/test_monitor.erl index 55b070e74a..7d742fee44 100644 --- a/tests/erlang_tests/test_monitor.erl +++ b/tests/erlang_tests/test_monitor.erl @@ -34,6 +34,33 @@ start() -> ok = test_monitor_demonitor_from_other(), ok = test_monitor_registered(), ok = test_monitor_registered_noproc(), + + AliasesAvailable = + case erlang:system_info(machine) of + "ATOM" -> true; + "BEAM" -> erlang:system_info(otp_release) >= "24" + end, + if + AliasesAvailable -> + ok = test_alias(), + ok = test_multiple_aliases(), + ok = test_multiple_unaliases(), + ok = test_unalias_from_wrong_process(), + ok = test_monitor_alias_dead_process(), + ok = test_monitor_multiple_aliases_monitors(fun spawn_monitor/2), + ok = test_monitor_multiple_aliases_monitors(fun spawn_and_monitor/2), + ok = test_monitor_alias_demonitor(fun spawn_monitor/2), + ok = test_monitor_alias_demonitor(fun spawn_and_monitor/2), + ok = test_monitor_alias_explicit_unalias(fun spawn_monitor/2), + ok = test_monitor_alias_explicit_unalias(fun spawn_and_monitor/2), + ok = test_monitor_alias_reply_demonitor(fun spawn_monitor/2), + ok = test_monitor_alias_reply_demonitor(fun spawn_and_monitor/2), + ok = test_monitor_down_alias(fun spawn_monitor/2), + ok = test_monitor_down_alias(fun spawn_and_monitor/2), + ok; + true -> + ok + end, 0. test_monitor_normal() -> @@ -229,7 +256,131 @@ test_monitor_demonitor_from_other() -> end, ok. +test_alias() -> + P = spawn_opt(fun echo_loop/0, []), + Alias = erlang:alias(), + do_test_alias(P, Alias), + ok. + +test_multiple_aliases() -> + P = spawn_opt(fun echo_loop/0, []), + A1 = erlang:alias(), + A2 = erlang:alias(), + A3 = erlang:alias(), + do_test_alias(P, A1), + do_test_alias(P, A3), + do_test_alias(P, A2), + ok. + +test_multiple_unaliases() -> + A = erlang:alias(), + true = erlang:unalias(A), + false = erlang:unalias(A), + false = erlang:unalias(A), + ok. + +test_unalias_from_wrong_process() -> + A = erlang:alias(), + TestProcess = self(), + spawn_opt(fun() -> TestProcess ! erlang:unalias(A) end, [link]), + false = recv_one(), + P = spawn_opt(fun echo_loop/0, []), + do_test_alias(P, A), + ok. + +do_test_alias(P, Alias) -> + do_test_alias(P, Alias, fun erlang:unalias/1). + +do_test_alias(P, Alias, UnaliasFun) -> + Ref = make_ref(), + P ! {{m1, Ref}, Alias}, + {m1, Ref} = recv_one(), + UnaliasFun(Alias), + P ! {{m2, Ref}, Alias}, + P ! {{m3, Ref}, self()}, + {m3, Ref} = recv_one(), + ok. + +test_monitor_alias_demonitor(SpawnFun) -> + {P, Mon} = SpawnFun(fun echo_loop/0, [{alias, demonitor}]), + do_test_alias(P, Mon, fun demonitor/1), + ok. + +test_monitor_alias_explicit_unalias(SpawnFun) -> + {P, Mon} = SpawnFun(fun echo_loop/0, [{alias, explicit_unalias}]), + P ! {m1, Mon}, + m1 = recv_one(), + demonitor(Mon), + do_test_alias(P, Mon), + ok. + +test_monitor_alias_reply_demonitor(SpawnFun) -> + {P, Mon} = SpawnFun(fun echo_loop/0, [{alias, reply_demonitor}]), + do_test_alias(P, Mon, fun(_Mon) -> ok end), + ok. + +test_monitor_down_alias(SpawnFun) -> + {P, Mon} = SpawnFun(fun echo_loop/0, [{alias, demonitor}]), + erlang:unalias(Mon), + P ! {m1, Mon}, + P ! {m2, self()}, + m2 = recv_one(), + P ! quit, + {'DOWN', Mon, process, P, normal} = recv_one(), + ok. + +test_monitor_multiple_aliases_monitors(SpawnFun) -> + {P, Mon1} = SpawnFun(fun echo_loop/0, [{alias, demonitor}]), + Mon2 = erlang:monitor(process, P, [{alias, reply_demonitor}]), + Mon3 = erlang:monitor(process, P, [{alias, explicit_unalias}]), + Mon4 = erlang:monitor(process, P), + A1 = erlang:alias(), + A2 = erlang:alias(), + do_test_alias(P, A2), + do_test_alias(P, Mon3), + do_test_alias(P, A1), + do_test_alias(P, Mon1, fun demonitor/1), + P ! quit, + {'DOWN', Mon2, process, P, normal} = recv_one(), + {'DOWN', Mon3, process, P, normal} = recv_one(), + {'DOWN', Mon4, process, P, normal} = recv_one(), + ok. + +test_monitor_alias_dead_process() -> + {P, Mon0} = spawn_opt(fun() -> ok end, [monitor]), + {'DOWN', Mon0, process, P, normal} = recv_one(), + Mon1 = erlang:monitor(process, P, [{alias, demonitor}]), + {'DOWN', Mon1, process, P, noproc} = recv_one(), + Mon2 = erlang:monitor(process, P, [{alias, reply_demonitor}]), + {'DOWN', Mon2, process, P, noproc} = recv_one(), + Mon3 = erlang:monitor(process, P, [{alias, explicit_unalias}]), + {'DOWN', Mon3, process, P, noproc} = recv_one(), + ok. + +spawn_monitor(LoopFun, Opts) -> + spawn_opt(LoopFun, [{monitor, Opts}]). + +spawn_and_monitor(LoopFun, Opts) -> + P = spawn_opt(LoopFun, []), + Mon = erlang:monitor(process, P, Opts), + {P, Mon}. + normal_loop() -> receive {Caller, quit} -> Caller ! {self(), finished} end. + +echo_loop() -> + receive + quit -> + ok; + {Msg, ReplyTo} -> + ReplyTo ! Msg, + echo_loop() + end. + +recv_one() -> + receive + Msg -> Msg + after 500 -> timeout + end. From 7fe164201e2f78fccdd45148e74bd070375cd391 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Mon, 22 Dec 2025 12:55:53 +0100 Subject: [PATCH 02/23] Add RefData, fix STM32 tests Signed-off-by: Mateusz Front --- src/libAtomVM/nifs.c | 6 +- src/libAtomVM/term.h | 32 ++- .../components/avm_builtins/network_driver.c | 8 +- .../components/avm_builtins/socket_driver.c | 184 +++++++++--------- .../components/avm_builtins/uart_driver.c | 32 +-- .../generic_unix/lib/socket_driver.c | 1 + src/platforms/rp2/src/lib/networkdriver.c | 7 +- 7 files changed, 152 insertions(+), 118 deletions(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 8f559f6d1f..2bacd2ac93 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1203,7 +1203,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) { // msg is not in the port's heap NativeHandlerResult result = NativeContinue; - if (UNLIKELY(memory_ensure_free_opt(ctx, 12, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, 13, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { fprintf(stderr, "Unable to allocate sufficient memory for console driver.\n"); AVM_ABORT(); } @@ -1221,7 +1221,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) term pid = term_get_tuple_element(msg, 1); term ref = term_get_tuple_element(msg, 2); term req = term_get_tuple_element(msg, 3); - uint64_t ref_ticks = term_to_ref_ticks(ref); + RefData ref_data = term_to_ref_data(ref); if (is_tagged_tuple(req, PUT_CHARS_ATOM, 3)) { term chars = term_get_tuple_element(req, 2); @@ -1231,7 +1231,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) printf("%s", str); free(str); - term refcopy = term_from_ref_ticks(ref_ticks, &ctx->heap); + term refcopy = term_from_ref_data(ref_data, &ctx->heap); term reply = term_alloc_tuple(3, &ctx->heap); term_put_tuple_element(reply, 0, IO_REPLY_ATOM); diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index c8dbc17aef..00c2180ba3 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -152,7 +152,10 @@ extern "C" { #define BOXED_FUN_SIZE 3 #define FLOAT_SIZE (sizeof(float_term_t) / sizeof(term) + 1) #define REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) -#define TERM_BOXED_PROCESS_REF_SIZE (REF_SIZE + 1) +// FIXME: The required size is REF_SIZE + 1, but then it's equal to +// TERM_BOXED_REFERENCE_RESOURCE_SIZE on 32bit arch, and therefore +// the process ref is indistinguishable from resource ref there +#define TERM_BOXED_PROCESS_REF_SIZE 5 #define TERM_BOXED_PROCESS_REF_HEADER (((TERM_BOXED_PROCESS_REF_SIZE - 1) << 6) | TERM_BOXED_REF) #if TERM_BYTES == 8 #define EXTERNAL_PID_SIZE 3 @@ -248,6 +251,14 @@ extern "C" { typedef struct GlobalContext GlobalContext; #endif +typedef struct RefData RefData; + +struct RefData +{ + uint64_t ref_ticks; + int32_t process_id; +}; + typedef struct PrinterFun PrinterFun; typedef int (*printer_function_t)(PrinterFun *fun, const char *fmt, ...) PRINTF_FORMAT_ARGS(2, 3); @@ -2230,7 +2241,7 @@ static inline term term_make_process_reference(int32_t process_id, uint64_t ref_ boxed_value[0] = TERM_BOXED_PROCESS_REF_HEADER; #if TERM_BYTES == 4 - boxed_value[1] = (ref_ticks >> 4); + boxed_value[1] = (ref_ticks >> 32); boxed_value[2] = (ref_ticks & 0xFFFFFFFF); boxed_value[3] = process_id; @@ -2258,6 +2269,23 @@ static inline uint32_t term_process_ref_to_process_id(term rt) #endif } +static inline RefData term_to_ref_data(term t) +{ + RefData ref_data; + ref_data.ref_ticks = term_to_ref_ticks(t); + ref_data.process_id = term_is_process_reference(t) ? term_process_ref_to_process_id(t) : 0; + return ref_data; +} + +static inline term term_from_ref_data(RefData ref_data, Heap *heap) +{ + if (ref_data.process_id) { + return term_make_process_reference(ref_data.process_id, ref_data.ref_ticks, heap); + } else { + return term_from_ref_ticks(ref_data.ref_ticks, heap); + } +} + /** * @brief Make an external pid term from node, process_id, serial and creation * diff --git a/src/platforms/esp32/components/avm_builtins/network_driver.c b/src/platforms/esp32/components/avm_builtins/network_driver.c index c67d481f06..82c8cc1dbb 100644 --- a/src/platforms/esp32/components/avm_builtins/network_driver.c +++ b/src/platforms/esp32/components/avm_builtins/network_driver.c @@ -58,7 +58,7 @@ #define TCPIP_HOSTNAME_MAX_SIZE 255 #define TAG "network_driver" -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE) static const char *const ap_atom = ATOM_STR("\x2", "ap"); static const char *const ap_channel_atom = ATOM_STR("\xA", "ap_channel"); @@ -137,7 +137,7 @@ static term tuple_from_addr(Heap *heap, uint32_t addr) static void send_term(Heap *heap, struct ClientData *data, term t) { - term ref = term_from_ref_ticks(data->ref_ticks, heap); + term ref = term_from_ref_data(data->ref_data, heap); term msg = term_alloc_tuple(2, heap); term_put_tuple_element(msg, 0, ref); term_put_tuple_element(msg, 1, t); @@ -1086,7 +1086,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { ESP_LOGE(TAG, "Unrecognized command: %x", cmd); // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; @@ -1096,7 +1096,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; diff --git a/src/platforms/esp32/components/avm_builtins/socket_driver.c b/src/platforms/esp32/components/avm_builtins/socket_driver.c index 00fc7d9e3c..2ac47d9520 100644 --- a/src/platforms/esp32/components/avm_builtins/socket_driver.c +++ b/src/platforms/esp32/components/avm_builtins/socket_driver.c @@ -49,7 +49,7 @@ #include #pragma GCC diagnostic pop -//#define ENABLE_TRACE 1 +// #define ENABLE_TRACE 1 #include "trace.h" #define TAG "socket_driver" @@ -69,7 +69,8 @@ static Context *socket_driver_create_port(GlobalContext *global, term opts); static NativeHandlerResult socket_consume_mailbox(Context *ctx); -static const char *const tcp_error_atom = "\x9" "tcp_error"; +static const char *const tcp_error_atom = "\x9" + "tcp_error"; static const char *const netconn_event_internal = ATOM_STR("\x1E", "$atomvm_netconn_event_internal"); static const char *gen_tcp_moniker_atom = ATOM_STR("\xC", "$avm_gen_tcp"); @@ -77,6 +78,11 @@ static const char *native_tcp_module_atom = ATOM_STR("\xC", "gen_tcp_inet"); static const char *gen_udp_moniker_atom = ATOM_STR("\xC", "$avm_gen_udp"); static const char *native_udp_module_atom = ATOM_STR("\xC", "gen_udp_inet"); +static const RefData no_ref_data = { + .process_id = 0, + .ref_ticks = 0, +}; + static inline term create_socket_wrapper(term pid, const char *moniker_atom, const char *module_atom, Heap *heap, GlobalContext *global) { term tuple = term_alloc_tuple(3, heap); @@ -146,7 +152,7 @@ static term socket_addr_to_tuple(Heap *heap, ip_addr_t *addr) break; } case IPADDR_TYPE_V6: - //TODO: implement IPv6 + // TODO: implement IPv6 addr_tuple = term_invalid_term(); break; @@ -178,7 +184,7 @@ struct SocketData int32_t controlling_process_pid; int32_t passive_receiver_process_pid; - uint64_t passive_ref_ticks; + RefData passive_ref_data; int avail_bytes; @@ -207,7 +213,7 @@ struct TCPServerAccepter { struct ListHead accepter_head; int32_t accepting_process_pid; - uint64_t ref_ticks; + RefData ref_data; }; struct UDPSocketData @@ -224,7 +230,6 @@ struct NetconnEvent u16_t len; }; - struct ReadyConnection { struct ListHead ready_connection_head; @@ -306,7 +311,7 @@ EventListener *socket_events_handler(GlobalContext *glb, EventListener *listener } else { // Add it to ready_connections TRACE("Got event for unknown conn: %p, len = %d adding to ready connections list\n", (void *) event.netconn, event.len); - struct ReadyConnection *ready = (struct ReadyConnection *) malloc(sizeof (struct ReadyConnection)); + struct ReadyConnection *ready = (struct ReadyConnection *) malloc(sizeof(struct ReadyConnection)); ready->netconn = event.netconn; ready->len = event.len; list_append(&platform->ready_connections, &ready->ready_connection_head); @@ -392,7 +397,7 @@ static void socket_data_init(struct SocketData *data, Context *ctx, struct netco list_append(sockets, &data->sockets_head); data->passive_receiver_process_pid = 0; - data->passive_ref_ticks = 0; + data->passive_ref_data = no_ref_data; ctx->platform_data = data; } @@ -438,12 +443,12 @@ static struct UDPSocketData *udp_socket_data_new(Context *ctx, struct netconn *c } // When this method is called, ensure free was called with REPLY_SIZE -#define REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) -static void do_send_reply(Context *ctx, term reply, uint64_t ref_ticks, int32_t pid) +#define REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE) +static void do_send_reply(Context *ctx, term reply, RefData ref_data, int32_t pid) { GlobalContext *glb = ctx->global; term reply_tuple = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(reply_tuple, 0, term_from_ref_ticks(ref_ticks, &ctx->heap)); + term_put_tuple_element(reply_tuple, 0, term_from_ref_data(ref_data, &ctx->heap)); term_put_tuple_element(reply_tuple, 1, reply); globalcontext_send_message(glb, pid, reply_tuple); } @@ -478,7 +483,7 @@ static term lwip_error_atom(GlobalContext *glb, err_t status) } } -static void do_send_error_reply(Context *ctx, err_t status, uint64_t ref_ticks, int32_t pid) +static void do_send_error_reply(Context *ctx, err_t status, RefData ref_data, int32_t pid) { GlobalContext *glb = ctx->global; if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) + REPLY_SIZE) != MEMORY_GC_OK)) { @@ -488,10 +493,10 @@ static void do_send_error_reply(Context *ctx, err_t status, uint64_t ref_ticks, term error_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(error_tuple, 0, ERROR_ATOM); term_put_tuple_element(error_tuple, 1, reason_atom); - do_send_reply(ctx, error_tuple, ref_ticks, pid); + do_send_reply(ctx, error_tuple, ref_data, pid); } -static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, uint64_t ref_ticks, int32_t pid) +static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, RefData ref_data, int32_t pid) { TRACE("Going to accept a TCP connection\n"); GlobalContext *glb = ctx->global; @@ -506,7 +511,7 @@ static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, uint err_t status = netconn_accept(tcp_data->socket_data.conn, &accepted_conn); if (UNLIKELY(status != ERR_OK)) { socket_data_postinit(platform); - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } @@ -557,8 +562,7 @@ static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, uint globalcontext_send_message(glb, new_ctx->process_id, message); } - do_send_reply(ctx, result_tuple, ref_ticks, pid); - + do_send_reply(ctx, result_tuple, ref_data, pid); } static void do_accept(Context *ctx, const GenMessage *gen_message) @@ -566,26 +570,26 @@ static void do_accept(Context *ctx, const GenMessage *gen_message) struct TCPServerSocketData *tcp_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (tcp_data->ready_connections) { TRACE("accepting existing connections.\n"); - accept_conn(ctx, tcp_data, ref_ticks, pid); + accept_conn(ctx, tcp_data, ref_data, pid); tcp_data->ready_connections--; } else { struct TCPServerAccepter *accepter = malloc(sizeof(struct TCPServerAccepter)); accepter->accepting_process_pid = pid; - accepter->ref_ticks = ref_ticks; + accepter->ref_data = ref_data; list_append(&tcp_data->accepters_list_head, &accepter->accepter_head); } } static void do_send_passive_reply(Context *ctx, struct SocketData *socket_data, term reply) { - do_send_reply(ctx, reply, socket_data->passive_ref_ticks, socket_data->passive_receiver_process_pid); + do_send_reply(ctx, reply, socket_data->passive_ref_data, socket_data->passive_receiver_process_pid); socket_data->passive_receiver_process_pid = 0; - socket_data->passive_ref_ticks = 0; + socket_data->passive_ref_data = no_ref_data; } static void do_send_socket_error(Context *ctx, err_t status) @@ -609,9 +613,9 @@ static void do_send_socket_error(Context *ctx, err_t status) globalcontext_send_message(glb, socket_data->controlling_process_pid, result_tuple); } } else { - do_send_error_reply(ctx, status, socket_data->passive_ref_ticks, socket_data->passive_receiver_process_pid); + do_send_error_reply(ctx, status, socket_data->passive_ref_data, socket_data->passive_receiver_process_pid); socket_data->passive_receiver_process_pid = 0; - socket_data->passive_ref_ticks = 0; + socket_data->passive_ref_data = no_ref_data; } } @@ -655,7 +659,7 @@ static void do_tcp_server_netconn_event(Context *ctx) } if (accepter) { - accept_conn(ctx, tcp_data, accepter->ref_ticks, accepter->accepting_process_pid); + accept_conn(ctx, tcp_data, accepter->ref_data, accepter->accepting_process_pid); free(accepter); } else { tcp_data->ready_connections++; @@ -763,15 +767,11 @@ static NativeHandlerResult do_receive_data(Context *ctx) netbuf_delete(buf); - if (socket_data->active) { term active_tuple = term_alloc_tuple(socket_data->type == TCPClientSocket ? 3 : 5, &ctx->heap); term_put_tuple_element(active_tuple, 0, socket_data->type == TCPClientSocket ? TCP_ATOM : UDP_ATOM); term socket_pid = term_port_from_local_process_id(ctx->process_id); - term socket_wrapper = - socket_data->type == UDPSocket ? - create_udp_socket_wrapper(socket_pid, &ctx->heap, ctx->global) : - create_tcp_socket_wrapper(socket_pid, &ctx->heap, ctx->global); + term socket_wrapper = socket_data->type == UDPSocket ? create_udp_socket_wrapper(socket_pid, &ctx->heap, ctx->global) : create_tcp_socket_wrapper(socket_pid, &ctx->heap, ctx->global); term_put_tuple_element(active_tuple, 1, socket_wrapper); if (socket_data->type == TCPClientSocket) { term_put_tuple_element(active_tuple, 2, recv_term); @@ -782,9 +782,9 @@ static NativeHandlerResult do_receive_data(Context *ctx) } globalcontext_send_message(ctx->global, socket_data->controlling_process_pid, active_tuple); TRACE("sent received to active process (pid=%d): ", (int) socket_data->controlling_process_pid); - #ifdef ENABLE_TRACE - term_display(stdout, active_tuple, ctx); - #endif +#ifdef ENABLE_TRACE + term_display(stdout, active_tuple, ctx); +#endif TRACE("\n"); } else { term ok_tuple = term_alloc_tuple(2, &ctx->heap); @@ -792,9 +792,9 @@ static NativeHandlerResult do_receive_data(Context *ctx) term_put_tuple_element(ok_tuple, 1, recv_term); do_send_passive_reply(ctx, socket_data, ok_tuple); TRACE("sent received to passive caller (pid=%d): ", (int) socket_data->passive_receiver_process_pid); - #ifdef ENABLE_TRACE - term_display(stdout, ok_tuple, ctx); - #endif +#ifdef ENABLE_TRACE + term_display(stdout, ok_tuple, ctx); +#endif TRACE("\n"); } @@ -862,7 +862,7 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) struct ESP32PlatformData *platform = glb->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); return; @@ -877,25 +877,25 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) bool ok = term_is_local_pid(controlling_process_term); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } int32_t controlling_process_pid = term_to_local_process_id(controlling_process_term); int ok_int; char *address_string = interop_term_to_string(address_term, &ok_int); if (UNLIKELY(!ok_int)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } avm_int_t port = term_to_int(port_term); bool active = bool_term_to_bool(active_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } bool binary = bool_term_to_bool(binary_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } @@ -906,12 +906,12 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) TRACE("tcp: connecting to: %s\n", address_string); ip_addr_t remote_ip; - //TODO: use dns_gethostbyname instead + // TODO: use dns_gethostbyname instead err_t status = netconn_gethostbyname(address_string, &remote_ip); if (UNLIKELY(status != ERR_OK)) { free(address_string); TRACE("tcp: host resolution failed.\n"); - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } @@ -929,7 +929,7 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) status = netconn_connect(conn, &remote_ip, port); if (UNLIKELY(status != ERR_OK)) { TRACE("tcp: failed connect: %i\n", status); - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } @@ -943,7 +943,7 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) tcp_data->socket_data.active = active; tcp_data->socket_data.binary = binary; - do_send_reply(ctx, OK_ATOM, ref_ticks, pid); + do_send_reply(ctx, OK_ATOM, ref_data, pid); } static void do_listen(Context *ctx, const GenMessage *gen_message) @@ -952,7 +952,7 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) struct ESP32PlatformData *platform = glb->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); return; @@ -970,12 +970,12 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) bool ok; bool active = bool_term_to_bool(active_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } bool binary = bool_term_to_bool(binary_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } @@ -987,7 +987,7 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) err_t status = netconn_bind(conn, IP_ADDR_ANY, port); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } @@ -995,13 +995,13 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) u16_t nport; status = netconn_getaddr(conn, &naddr, &nport, 1); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } status = netconn_listen_with_backlog(conn, backlog); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } @@ -1018,7 +1018,7 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_ticks, pid); + do_send_reply(ctx, OK_ATOM, ref_data, pid); } void do_udp_open(Context *ctx, const GenMessage *gen_message) @@ -1027,7 +1027,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) struct ESP32PlatformData *platform = glb->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); return; @@ -1041,19 +1041,19 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) bool ok = term_is_local_pid(controlling_process_term); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } int32_t controlling_process_pid = term_to_local_process_id(controlling_process_term); avm_int_t port = term_to_int(port_term); bool active = bool_term_to_bool(active_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } bool binary = bool_term_to_bool(binary_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } @@ -1076,7 +1076,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) if (port != 0) { err_t status = netconn_bind(conn, IP_ADDR_ANY, port); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } } @@ -1085,7 +1085,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) u16_t nport; err_t status = netconn_getaddr(conn, &naddr, &nport, 1); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } udp_data->socket_data.port = nport; @@ -1093,7 +1093,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_ticks, pid); + do_send_reply(ctx, OK_ATOM, ref_data, pid); } // Required for compatibility with existing erlang libraries @@ -1125,7 +1125,7 @@ static void do_send(Context *ctx, const GenMessage *gen_message) struct TCPServerSocketData *tcp_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); @@ -1167,7 +1167,7 @@ static void do_send(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_ticks, pid); + do_send_reply(ctx, OK_ATOM, ref_data, pid); } static void do_sendto(Context *ctx, const GenMessage *gen_message) @@ -1175,7 +1175,7 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) struct UDPSocketData *udp_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 4)) { ESP_LOGW(TAG, "Received invalid message."); @@ -1191,10 +1191,10 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) case InteropOk: break; case InteropMemoryAllocFail: - do_send_error_reply(ctx, ERR_MEM, ref_ticks, pid); + do_send_error_reply(ctx, ERR_MEM, ref_data, pid); return; case InteropBadArg: - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } void *buffer = malloc(buffer_size); @@ -1203,11 +1203,11 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) break; case InteropMemoryAllocFail: free(buffer); - do_send_error_reply(ctx, ERR_MEM, ref_ticks, pid); + do_send_error_reply(ctx, ERR_MEM, ref_data, pid); return; case InteropBadArg: free(buffer); - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); return; } @@ -1221,7 +1221,7 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(status != ERR_OK)) { netbuf_delete(sendbuf); free(buffer); - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } @@ -1230,14 +1230,14 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) free(buffer); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_ticks, pid); + do_send_error_reply(ctx, status, ref_data, pid); return; } if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_ticks, pid); + do_send_reply(ctx, OK_ATOM, ref_data, pid); } static void do_close(Context *ctx, const GenMessage *gen_message) @@ -1245,7 +1245,7 @@ static void do_close(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) + REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); @@ -1263,11 +1263,11 @@ static void do_close(Context *ctx, const GenMessage *gen_message) synclist_remove(&platform->sockets, &socket_data->sockets_head); if (UNLIKELY(close_disconnect_res != ERR_OK)) { - do_send_error_reply(ctx, close_disconnect_res, ref_ticks, pid); + do_send_error_reply(ctx, close_disconnect_res, ref_data, pid); } else if (UNLIKELY(delete_res != ERR_OK)) { - do_send_error_reply(ctx, delete_res, ref_ticks, pid); + do_send_error_reply(ctx, delete_res, ref_data, pid); } else { - do_send_reply(ctx, OK_ATOM, ref_ticks, pid); + do_send_reply(ctx, OK_ATOM, ref_data, pid); } } @@ -1276,16 +1276,16 @@ static NativeHandlerResult do_recvfrom(Context *ctx, const GenMessage *gen_messa struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); // We cannot stack blocked queries if (socket_data->passive_receiver_process_pid != 0) { - do_send_error_reply(ctx, ERR_ALREADY, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ALREADY, ref_data, pid); return NativeContinue; } socket_data->passive_receiver_process_pid = pid; - socket_data->passive_ref_ticks = ref_ticks; + socket_data->passive_ref_data = ref_data; // There may be nothing to read. if (socket_data->avail_bytes <= 0 && !socket_data->read_condition) { @@ -1300,10 +1300,10 @@ static void do_get_port(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (socket_data->port == 0) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); } else { if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) + REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); @@ -1311,7 +1311,7 @@ static void do_get_port(Context *ctx, const GenMessage *gen_message) term ok_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(ok_tuple, 0, OK_ATOM); term_put_tuple_element(ok_tuple, 1, term_from_int(socket_data->port)); - do_send_reply(ctx, ok_tuple, ref_ticks, pid); + do_send_reply(ctx, ok_tuple, ref_data, pid); } } @@ -1320,14 +1320,14 @@ static void do_sockname(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); ip_addr_t addr; u16_t port; err_t result = netconn_addr(socket_data->conn, &addr, &port); term return_msg; if (result != ERR_OK) { - do_send_error_reply(ctx, result, ref_ticks, pid); + do_send_error_reply(ctx, result, ref_data, pid); } else { if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + SOCKET_INET_ADDR + REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); @@ -1340,7 +1340,7 @@ static void do_sockname(Context *ctx, const GenMessage *gen_message) term_put_tuple_element(address_port_term, 1, port_term); term_put_tuple_element(return_msg, 0, OK_ATOM); term_put_tuple_element(return_msg, 1, address_port_term); - do_send_reply(ctx, return_msg, ref_ticks, pid); + do_send_reply(ctx, return_msg, ref_data, pid); } } @@ -1349,14 +1349,14 @@ static void do_peername(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); ip_addr_t addr; u16_t port; err_t result = netconn_peer(socket_data->conn, &addr, &port); term return_msg; if (result != ERR_OK) { - do_send_error_reply(ctx, result, ref_ticks, pid); + do_send_error_reply(ctx, result, ref_data, pid); } else { // {ok, {{A,B,C,D}, Port}} if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + SOCKET_INET_ADDR + REPLY_SIZE) != MEMORY_GC_OK)) { @@ -1370,7 +1370,7 @@ static void do_peername(Context *ctx, const GenMessage *gen_message) term_put_tuple_element(address_port_term, 1, port_term); term_put_tuple_element(return_msg, 0, OK_ATOM); term_put_tuple_element(return_msg, 1, address_port_term); - do_send_reply(ctx, return_msg, ref_ticks, pid); + do_send_reply(ctx, return_msg, ref_data, pid); } } @@ -1379,7 +1379,7 @@ static void do_controlling_process(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); + RefData ref_data = term_to_ref_data(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); @@ -1387,7 +1387,7 @@ static void do_controlling_process(Context *ctx, const GenMessage *gen_message) } term new_pid_term = term_get_tuple_element(gen_message->req, 1); if (UNLIKELY(!term_is_local_pid(new_pid_term))) { - do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); + do_send_error_reply(ctx, ERR_ARG, ref_data, pid); } else { term return_msg; @@ -1404,7 +1404,7 @@ static void do_controlling_process(Context *ctx, const GenMessage *gen_message) socket_data->controlling_process_pid = new_pid; return_msg = OK_ATOM; } - do_send_reply(ctx, return_msg, ref_ticks, pid); + do_send_reply(ctx, return_msg, ref_data, pid); } } @@ -1418,9 +1418,9 @@ static NativeHandlerResult socket_consume_mailbox(Context *ctx) term msg = message->message; TRACE("message: "); - #ifdef ENABLE_TRACE - term_display(stdout, msg, ctx); - #endif +#ifdef ENABLE_TRACE + term_display(stdout, msg, ctx); +#endif TRACE("\n"); if (term_is_tuple(msg) && term_get_tuple_element(msg, 0) == globalcontext_make_atom(glb, netconn_event_internal)) { @@ -1445,7 +1445,7 @@ static NativeHandlerResult socket_consume_mailbox(Context *ctx) term cmd_name = term_get_tuple_element(gen_message.req, 0); switch (cmd_name) { - //TODO: remove this + // TODO: remove this case INIT_ATOM: TRACE("init\n"); do_init(ctx, &gen_message); diff --git a/src/platforms/esp32/components/avm_builtins/uart_driver.c b/src/platforms/esp32/components/avm_builtins/uart_driver.c index 3f73369876..c7927668d3 100644 --- a/src/platforms/esp32/components/avm_builtins/uart_driver.c +++ b/src/platforms/esp32/components/avm_builtins/uart_driver.c @@ -56,7 +56,6 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx); #define TAG "uart_driver" #define UART_BUF_SIZE 256 -#define NO_REF 0 #define NO_READER term_invalid_term() #define PIN_ERROR -2 @@ -64,12 +63,17 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx); #define GPIO_NUM_MAX SOC_GPIO_PIN_COUNT #endif +static const RefData no_ref_data = { + .process_id = 0, + .ref_ticks = 0, +}; + struct UARTData { QueueHandle_t rxqueue; EventListener listener; term reader_process_pid; - uint64_t reader_ref_ticks; + RefData reader_ref_data; uint8_t uart_num; #ifndef AVM_NO_SMP Mutex *reader_lock; @@ -107,11 +111,11 @@ static const AtomStringIntPair cmd_table[] = { SELECT_INT_DEFAULT(UARTInvalidCmd) }; -static void safe_update_reader_data(struct UARTData *uart_data, term pid, uint64_t ref_ticks) +static void safe_update_reader_data(struct UARTData *uart_data, term pid, RefData ref_data) { SMP_MUTEX_LOCK(uart_data->reader_lock); uart_data->reader_process_pid = pid; - uart_data->reader_ref_ticks = ref_ticks; + uart_data->reader_ref_data = ref_data; SMP_MUTEX_UNLOCK(uart_data->reader_lock); } @@ -128,7 +132,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen int bin_size = term_binary_heap_size(event.size); Heap heap; - if (UNLIKELY(memory_init_heap(&heap, bin_size + REF_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_init_heap(&heap, bin_size + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); AVM_ABORT(); } @@ -141,7 +145,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen term_put_tuple_element(ok_tuple, 0, OK_ATOM); term_put_tuple_element(ok_tuple, 1, bin); - term ref = term_from_ref_ticks(uart_data->reader_ref_ticks, &heap); + term ref = term_from_ref_data(uart_data->reader_ref_data, &heap); term result_tuple = term_alloc_tuple(2, &heap); term_put_tuple_element(result_tuple, 0, ref); @@ -151,7 +155,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen globalcontext_send_message(glb, local_pid, result_tuple); memory_destroy_heap(&heap, glb); - safe_update_reader_data(uart_data, NO_READER, NO_REF); + safe_update_reader_data(uart_data, NO_READER, no_ref_data); } break; case UART_FIFO_OVF: @@ -323,7 +327,7 @@ Context *uart_driver_create_port(GlobalContext *global, term opts) uart_data->listener.handler = uart_interrupt_callback; sys_register_listener(global, &uart_data->listener); uart_data->reader_process_pid = term_invalid_term(); - uart_data->reader_ref_ticks = 0; + uart_data->reader_ref_data = no_ref_data; uart_data->uart_num = uart_num; ctx->native_handler = uart_driver_consume_mailbox; ctx->platform_data = uart_data; @@ -353,7 +357,7 @@ static void uart_driver_do_read(Context *ctx, GenMessage gen_message) struct UARTData *uart_data = ctx->platform_data; term pid = gen_message.pid; term ref = gen_message.ref; - uint64_t ref_ticks = term_to_ref_ticks(ref); + RefData ref_data = term_to_ref_data(ref); int local_pid = term_to_local_process_id(pid); @@ -391,7 +395,7 @@ static void uart_driver_do_read(Context *ctx, GenMessage gen_message) port_send_reply(ctx, pid, ref, ok_tuple); } else { - safe_update_reader_data(uart_data, pid, ref_ticks); + safe_update_reader_data(uart_data, pid, ref_data); } } @@ -399,7 +403,7 @@ static void uart_driver_do_cancel_read(Context *ctx, GenMessage gen_message) { struct UARTData *uart_data = ctx->platform_data; - safe_update_reader_data(uart_data, NO_READER, NO_REF); + safe_update_reader_data(uart_data, NO_READER, no_ref_data); term pid = gen_message.pid; term ref = gen_message.ref; @@ -506,12 +510,12 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx) return NativeContinue; } - uint64_t ref_ticks = term_to_ref_ticks(gen_message.ref); + RefData ref_data = term_to_ref_data(gen_message.ref); int local_pid = term_to_local_process_id(gen_message.pid); if (is_closed) { - if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + REF_SIZE) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + TERM_BOXED_PROCESS_REF_SIZE) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "[uart_driver_consume_mailbox] Failed to allocate space for error tuple"); globalcontext_send_message(glb, local_pid, OUT_OF_MEMORY_ATOM); } @@ -521,7 +525,7 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx) term_put_tuple_element(error_tuple, 1, NOPROC_ATOM); term result_tuple = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(result_tuple, 0, term_from_ref_ticks(ref_ticks, &ctx->heap)); + term_put_tuple_element(result_tuple, 0, term_from_ref_data(ref_data, &ctx->heap)); term_put_tuple_element(result_tuple, 1, error_tuple); globalcontext_send_message(glb, local_pid, result_tuple); diff --git a/src/platforms/generic_unix/lib/socket_driver.c b/src/platforms/generic_unix/lib/socket_driver.c index 7e9439622d..a46b99d162 100644 --- a/src/platforms/generic_unix/lib/socket_driver.c +++ b/src/platforms/generic_unix/lib/socket_driver.c @@ -67,6 +67,7 @@ typedef struct PassiveRecvListener size_t length; size_t buffer; term controlling_process; + // FIXME change to ref data uint64_t ref_ticks; } PassiveRecvListener; diff --git a/src/platforms/rp2/src/lib/networkdriver.c b/src/platforms/rp2/src/lib/networkdriver.c index bdef70eb75..a097eed582 100644 --- a/src/platforms/rp2/src/lib/networkdriver.c +++ b/src/platforms/rp2/src/lib/networkdriver.c @@ -41,7 +41,7 @@ #pragma GCC diagnostic pop -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE) #define DEFAULT_HOSTNAME_FMT "atomvm-%02x%02x%02x%02x%02x%02x" #define DEFAULT_HOSTNAME_SIZE (strlen("atomvm-") + 12 + 1) @@ -79,6 +79,7 @@ struct NetworkDriverData { GlobalContext *global; uint32_t owner_process_id; + // FIXME change to ref data uint64_t ref_ticks; int link_status; char *sntp_hostname; @@ -758,7 +759,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } @@ -767,7 +768,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } From 056bf5b6d02158bfeeb1811df347f9a6a1d3f222 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Tue, 20 Jan 2026 17:43:44 +0100 Subject: [PATCH 03/23] Make monitors keep RefData Signed-off-by: Mateusz Front --- src/libAtomVM/context.c | 46 ++++++++++++++++++++--------------------- src/libAtomVM/context.h | 16 +++++++------- src/libAtomVM/nifs.c | 33 +++++++++++++++-------------- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index afb58b0ea9..b0c5a49cca 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -432,7 +432,7 @@ void context_process_monitor_down_signal(Context *ctx, struct TermSignal *signal struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head); if (monitor->monitor_type == CONTEXT_MONITOR_MONITORING_LOCAL) { struct MonitorLocalMonitor *monitoring_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); - if (monitoring_monitor->monitor_obj == monitor_obj && monitoring_monitor->ref_ticks == ref_ticks) { + if (monitoring_monitor->monitor_obj == monitor_obj && monitoring_monitor->ref_data.ref_ticks == ref_ticks) { // Remove link list_remove(&monitor->monitor_list_head); free(monitoring_monitor); @@ -443,7 +443,7 @@ void context_process_monitor_down_signal(Context *ctx, struct TermSignal *signal } else if (monitor->monitor_type == CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME) { int32_t monitor_process_id = term_to_local_process_id(monitor_obj); struct MonitorLocalRegisteredNameMonitor *monitoring_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor); - if (monitoring_monitor->monitor_process_id == monitor_process_id && monitoring_monitor->ref_ticks == ref_ticks) { + if (monitoring_monitor->monitor_process_id == monitor_process_id && monitoring_monitor->ref_data.ref_ticks == ref_ticks) { // Remove link list_remove(&monitor->monitor_list_head); @@ -719,7 +719,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) Context *target = globalcontext_get_process_nolock(glb, local_process_id); if (LIKELY(target != NULL)) { // target can be null if we didn't process a MonitorDownSignal - mailbox_send_ref_signal(target, DemonitorSignal, monitoring_monitor->ref_ticks); + mailbox_send_ref_signal(target, DemonitorSignal, monitoring_monitor->ref_data.ref_ticks); } free(monitoring_monitor); break; @@ -731,7 +731,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) Context *target = globalcontext_get_process_nolock(glb, local_process_id); if (LIKELY(target != NULL)) { // target can be null if we didn't process a MonitorDownSignal - mailbox_send_ref_signal(target, DemonitorSignal, monitoring_monitor->ref_ticks); + mailbox_send_ref_signal(target, DemonitorSignal, monitoring_monitor->ref_data.ref_ticks); } free(monitoring_monitor); break; @@ -787,7 +787,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) AVM_ABORT(); } // Prepare the message on ctx's heap which will be freed afterwards. - term ref = term_make_process_reference(target->process_id, monitored_monitor->ref_ticks, &ctx->heap); + term ref = term_from_ref_data(monitored_monitor->ref_data, &ctx->heap); term port_or_process = term_pid_or_port_from_context(ctx); term port_or_process_atom @@ -846,7 +846,7 @@ struct Monitor *monitor_link_new(term link_pid) } } -struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monitoring) +struct Monitor *monitor_new(term monitor_pid, RefData ref_data, bool is_monitoring) { struct MonitorLocalMonitor *monitor = malloc(sizeof(struct MonitorLocalMonitor)); if (IS_NULL_PTR(monitor)) { @@ -858,12 +858,12 @@ struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monito monitor->monitor.monitor_type = CONTEXT_MONITOR_MONITORED_LOCAL; } monitor->monitor_obj = monitor_pid; - monitor->ref_ticks = ref_ticks; + monitor->ref_data = ref_data; return &monitor->monitor; } -struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, uint64_t ref_ticks) +struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, RefData ref_data) { struct MonitorLocalRegisteredNameMonitor *monitor = malloc(sizeof(struct MonitorLocalRegisteredNameMonitor)); if (IS_NULL_PTR(monitor)) { @@ -872,19 +872,19 @@ struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, t monitor->monitor.monitor_type = CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME; monitor->monitor_process_id = monitor_process_id; monitor->monitor_name = monitor_name; - monitor->ref_ticks = ref_ticks; + monitor->ref_data = ref_data; return &monitor->monitor; } -struct Monitor *monitor_alias_new(uint64_t ref_ticks, enum ContextMonitorAliasType alias_type) +struct Monitor *monitor_alias_new(RefData ref_data, enum ContextMonitorAliasType alias_type) { struct MonitorAlias *monitor = malloc(sizeof(struct MonitorAlias)); if (IS_NULL_PTR(monitor)) { return NULL; } monitor->monitor.monitor_type = CONTEXT_MONITOR_ALIAS; - monitor->ref_ticks = ref_ticks; + monitor->ref_data = ref_data; monitor->alias_type = alias_type; return &monitor->monitor; @@ -924,7 +924,7 @@ bool context_add_monitor(Context *ctx, struct Monitor *new_monitor) case CONTEXT_MONITOR_MONITORED_LOCAL: { struct MonitorLocalMonitor *new_local_monitor = CONTAINER_OF(new_monitor, struct MonitorLocalMonitor, monitor); struct MonitorLocalMonitor *existing_local_monitor = CONTAINER_OF(existing, struct MonitorLocalMonitor, monitor); - if (UNLIKELY(existing_local_monitor->monitor_obj == new_local_monitor->monitor_obj && existing_local_monitor->ref_ticks == new_local_monitor->ref_ticks)) { + if (UNLIKELY(existing_local_monitor->monitor_obj == new_local_monitor->monitor_obj && existing_local_monitor->ref_data.ref_ticks == new_local_monitor->ref_data.ref_ticks)) { free(new_local_monitor); return false; } @@ -935,7 +935,7 @@ bool context_add_monitor(Context *ctx, struct Monitor *new_monitor) struct MonitorLocalRegisteredNameMonitor *existing_local_registeredname_monitor = CONTAINER_OF(existing, struct MonitorLocalRegisteredNameMonitor, monitor); if (UNLIKELY(existing_local_registeredname_monitor->monitor_process_id == new_local_registeredname_monitor->monitor_process_id && existing_local_registeredname_monitor->monitor_name == new_local_registeredname_monitor->monitor_name - && existing_local_registeredname_monitor->ref_ticks == new_local_registeredname_monitor->ref_ticks)) { + && existing_local_registeredname_monitor->ref_data.ref_ticks == new_local_registeredname_monitor->ref_data.ref_ticks)) { free(new_local_registeredname_monitor); return false; } @@ -945,7 +945,7 @@ bool context_add_monitor(Context *ctx, struct Monitor *new_monitor) struct MonitorAlias *new_alias_monitor = CONTAINER_OF(new_monitor, struct MonitorAlias, monitor); struct MonitorAlias *existing_alias_monitor = CONTAINER_OF(existing, struct MonitorAlias, monitor); - if (UNLIKELY(existing_alias_monitor->alias_type == new_alias_monitor->alias_type && existing_alias_monitor->ref_ticks == new_alias_monitor->ref_ticks)) { + if (UNLIKELY(existing_alias_monitor->alias_type == new_alias_monitor->alias_type && existing_alias_monitor->ref_data.ref_ticks == new_alias_monitor->ref_data.ref_ticks)) { free(new_monitor); return false; } @@ -1096,7 +1096,7 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks) case CONTEXT_MONITOR_MONITORING_LOCAL: case CONTEXT_MONITOR_MONITORED_LOCAL: { struct MonitorLocalMonitor *local_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); - if (local_monitor->ref_ticks == ref_ticks) { + if (local_monitor->ref_data.ref_ticks == ref_ticks) { list_remove(&monitor->monitor_list_head); free(local_monitor); return; @@ -1105,7 +1105,7 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks) } case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: { struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor); - if (local_registeredname_monitor->ref_ticks == ref_ticks) { + if (local_registeredname_monitor->ref_data.ref_ticks == ref_ticks) { list_remove(&monitor->monitor_list_head); free(local_registeredname_monitor); return; @@ -1135,7 +1135,7 @@ struct MonitorAlias *context_find_alias(Context *ctx, uint64_t ref_ticks) struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head); if (monitor->monitor_type == CONTEXT_MONITOR_ALIAS) { struct MonitorAlias *alias_monitor = CONTAINER_OF(monitor, struct MonitorAlias, monitor); - if (alias_monitor->ref_ticks == ref_ticks) { + if (alias_monitor->ref_data.ref_ticks == ref_ticks) { return alias_monitor; } } @@ -1161,7 +1161,7 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori case CONTEXT_MONITOR_MONITORING_LOCAL: case CONTEXT_MONITOR_MONITORED_LOCAL: { struct MonitorLocalMonitor *local_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); - if (local_monitor->ref_ticks == ref_ticks) { + if (local_monitor->ref_data.ref_ticks == ref_ticks) { *is_monitoring = monitor->monitor_type == CONTEXT_MONITOR_MONITORING_LOCAL; return local_monitor->monitor_obj; } @@ -1169,7 +1169,7 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori } case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: { struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor); - if (local_registeredname_monitor->ref_ticks == ref_ticks) { + if (local_registeredname_monitor->ref_data.ref_ticks == ref_ticks) { *is_monitoring = true; return term_from_local_process_id(local_registeredname_monitor->monitor_process_id); } @@ -1304,13 +1304,13 @@ COLD_FUNC void context_dump(Context *ctx) struct MonitorLocalMonitor *monitoring_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); fprintf(stderr, "monitor to "); term_display(stderr, monitoring_monitor->monitor_obj, ctx); - fprintf(stderr, " ref=%lu", (long unsigned) monitoring_monitor->ref_ticks); + fprintf(stderr, " ref=%lu", (long unsigned) monitoring_monitor->ref_data.ref_ticks); fprintf(stderr, "\n"); break; } case CONTEXT_MONITOR_ALIAS: { struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); - fprintf(stderr, "has alias ref=%lu", (long unsigned) monitored_monitor->ref_ticks); + fprintf(stderr, "has alias ref=%lu", (long unsigned) monitored_monitor->ref_data.ref_ticks); fprintf(stderr, "\n"); break; } @@ -1318,7 +1318,7 @@ COLD_FUNC void context_dump(Context *ctx) struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); fprintf(stderr, "monitored by "); term_display(stderr, monitored_monitor->monitor_obj, ctx); - fprintf(stderr, " ref=%lu", (long unsigned) monitored_monitor->ref_ticks); + fprintf(stderr, " ref=%lu", (long unsigned) monitored_monitor->ref_data.ref_ticks); fprintf(stderr, "\n"); break; } @@ -1328,7 +1328,7 @@ COLD_FUNC void context_dump(Context *ctx) term_display(stderr, local_registeredname_monitor->monitor_name, ctx); fprintf(stderr, " ("); term_display(stderr, term_from_local_process_id(local_registeredname_monitor->monitor_process_id), ctx); - fprintf(stderr, ") ref=%lu", (long unsigned) local_registeredname_monitor->ref_ticks); + fprintf(stderr, ") ref=%lu", (long unsigned) local_registeredname_monitor->ref_data.ref_ticks); fprintf(stderr, "\n"); break; } diff --git a/src/libAtomVM/context.h b/src/libAtomVM/context.h index b33b1d70c3..0320f1101d 100644 --- a/src/libAtomVM/context.h +++ b/src/libAtomVM/context.h @@ -208,14 +208,14 @@ struct LinkLocalMonitor struct MonitorLocalMonitor { struct Monitor monitor; - uint64_t ref_ticks; + RefData ref_data; term monitor_obj; }; struct MonitorLocalRegisteredNameMonitor { struct Monitor monitor; - uint64_t ref_ticks; + RefData ref_data; int32_t monitor_process_id; term monitor_name; }; @@ -223,7 +223,7 @@ struct MonitorLocalRegisteredNameMonitor struct MonitorAlias { struct Monitor monitor; - uint64_t ref_ticks; + RefData ref_data; enum ContextMonitorAliasType alias_type; }; @@ -526,23 +526,23 @@ struct Monitor *monitor_link_new(term link_pid); * @brief Create a monitor on a process. * * @param monitor_pid monitored process - * @param ref_ticks reference of the monitor + * @param ref_data reference of the monitor * @param is_monitoring if ctx is the monitoring process * @return the allocated monitor or NULL if allocation failed */ -struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monitoring); +struct Monitor *monitor_new(term monitor_pid, RefData ref_data, bool is_monitoring); -struct Monitor *monitor_alias_new(uint64_t ref_ticks, enum ContextMonitorAliasType alias_type); +struct Monitor *monitor_alias_new(RefData ref_data, enum ContextMonitorAliasType alias_type); /** * @brief Create a monitor on a process by registered name. * * @param monitor_process_id monitored process id * @param monitor_name name of the monitor (atom) - * @param ref_ticks reference of the monitor + * @param ref_data reference of the monitor * @return the allocated monitor or NULL if allocation failed */ -struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, uint64_t ref_ticks); +struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, RefData ref_data); /** * @brief Create a resource monitor. diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 2bacd2ac93..81e0716f31 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1408,7 +1408,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free context_destroy(new_ctx); RAISE_ERROR(BADARG_ATOM); } - uint64_t ref_ticks = 0; + RefData ref_data = { .ref_ticks = 0, .process_id = ctx->process_id }; term new_pid = term_from_local_process_id(new_ctx->process_id); if (link_term == TRUE_ATOM) { @@ -1439,13 +1439,14 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free return term_invalid_term(); } // We can call context_add_monitor directly on new process because it's not started yet - ref_ticks = globalcontext_get_ref_ticks(ctx->global); - struct Monitor *new_monitor = monitor_new(term_from_local_process_id(ctx->process_id), ref_ticks, false); + ref_data.ref_ticks = globalcontext_get_ref_ticks(ctx->global); + + struct Monitor *new_monitor = monitor_new(term_from_local_process_id(ctx->process_id), ref_data, false); if (IS_NULL_PTR(new_monitor)) { context_destroy(new_ctx); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - struct Monitor *self_monitor = monitor_new(new_pid, ref_ticks, true); + struct Monitor *self_monitor = monitor_new(new_pid, ref_data, true); if (IS_NULL_PTR(self_monitor)) { free(new_monitor); context_destroy(new_ctx); @@ -1453,7 +1454,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free } struct Monitor *alias_monitor = NULL; if (is_alias) { - alias_monitor = monitor_alias_new(ref_ticks, alias_type); + alias_monitor = monitor_alias_new(ref_data, alias_type); if (IS_NULL_PTR(alias_monitor)) { free(new_monitor); free(self_monitor); @@ -1468,7 +1469,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free } } - if (ref_ticks) { + if (ref_data.ref_ticks) { int res_size = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free_opt(ctx, res_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { context_destroy(new_ctx); @@ -1477,7 +1478,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free scheduler_init_ready(new_ctx); - term ref = term_make_process_reference(ctx->process_id, ref_ticks, &ctx->heap); + term ref = term_from_ref_data(ref_data, &ctx->heap); term process_ref_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(process_ref_tuple, 0, new_pid); @@ -4471,19 +4472,19 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) if ((object_type == PROCESS_ATOM && target->native_handler != NULL) || (object_type == PORT_ATOM && target->native_handler == NULL)) { RAISE_ERROR(BADARG_ATOM); } - uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global); + RefData ref_data = { .process_id = ctx->process_id, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; term monitoring_pid = term_from_local_process_id(ctx->process_id); struct Monitor *self_monitor; if (term_is_atom(target_proc)) { - self_monitor = monitor_registeredname_monitor_new(local_process_id, target_proc, ref_ticks); + self_monitor = monitor_registeredname_monitor_new(local_process_id, target_proc, ref_data); } else { - self_monitor = monitor_new(target_pid, ref_ticks, true); + self_monitor = monitor_new(target_pid, ref_data, true); } if (IS_NULL_PTR(self_monitor)) { globalcontext_get_process_unlock(ctx->global, target); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - struct Monitor *other_monitor = monitor_new(monitoring_pid, ref_ticks, false); + struct Monitor *other_monitor = monitor_new(monitoring_pid, ref_data, false); if (IS_NULL_PTR(other_monitor)) { free(self_monitor); globalcontext_get_process_unlock(ctx->global, target); @@ -4491,7 +4492,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) } struct Monitor *alias_monitor = NULL; if (is_alias) { - alias_monitor = monitor_alias_new(ref_ticks, alias_type); + alias_monitor = monitor_alias_new(ref_data, alias_type); if (IS_NULL_PTR(alias_monitor)) { free(self_monitor); free(other_monitor); @@ -4511,7 +4512,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - return term_make_process_reference(ctx->process_id, ref_ticks, &ctx->heap); + return term_from_ref_data(ref_data, &ctx->heap); } static term nif_erlang_demonitor(Context *ctx, int argc, term argv[]) @@ -6678,9 +6679,9 @@ static term nif_erlang_alias(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global); - term process_ref = term_make_process_reference(ctx->process_id, ref_ticks, &ctx->heap); - struct Monitor *monitor = monitor_alias_new(ref_ticks, ContextMonitorAliasExplicitUnalias); + RefData ref_data = { .process_id = ctx->process_id, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + term process_ref = term_from_ref_data(ref_data, &ctx->heap); + struct Monitor *monitor = monitor_alias_new(ref_data, ContextMonitorAliasExplicitUnalias); if (IS_NULL_PTR(monitor)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } From 78111e112cfa3099ce3c12bc64c813592e6353e7 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 21 Jan 2026 11:00:25 +0100 Subject: [PATCH 04/23] Make non-alias monitors short refs Signed-off-by: Mateusz Front --- src/libAtomVM/nifs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 81e0716f31..add76cbe37 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1408,7 +1408,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free context_destroy(new_ctx); RAISE_ERROR(BADARG_ATOM); } - RefData ref_data = { .ref_ticks = 0, .process_id = ctx->process_id }; + RefData ref_data = { .ref_ticks = 0, .process_id = 0 }; term new_pid = term_from_local_process_id(new_ctx->process_id); if (link_term == TRUE_ATOM) { @@ -1454,6 +1454,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free } struct Monitor *alias_monitor = NULL; if (is_alias) { + ref_data.process_id = ctx->process_id; alias_monitor = monitor_alias_new(ref_data, alias_type); if (IS_NULL_PTR(alias_monitor)) { free(new_monitor); @@ -4472,7 +4473,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) if ((object_type == PROCESS_ATOM && target->native_handler != NULL) || (object_type == PORT_ATOM && target->native_handler == NULL)) { RAISE_ERROR(BADARG_ATOM); } - RefData ref_data = { .process_id = ctx->process_id, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + RefData ref_data = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = 0 }; term monitoring_pid = term_from_local_process_id(ctx->process_id); struct Monitor *self_monitor; if (term_is_atom(target_proc)) { @@ -4492,6 +4493,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) } struct Monitor *alias_monitor = NULL; if (is_alias) { + ref_data.process_id = ctx->process_id; alias_monitor = monitor_alias_new(ref_data, alias_type); if (IS_NULL_PTR(alias_monitor)) { free(self_monitor); From 18ff9e09cfe9e3f5b0da9effb1e78146e1a86c99 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 21 Jan 2026 11:12:03 +0100 Subject: [PATCH 05/23] Change reference sizes Signed-off-by: Mateusz Front --- src/libAtomVM/term.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 00c2180ba3..46491563d0 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -129,7 +129,14 @@ extern "C" { #define TERM_BOXED_REFC_BINARY_SIZE 6 #define TERM_BOXED_BIN_MATCH_STATE_SIZE 4 #define TERM_BOXED_SUB_BINARY_SIZE 4 +#if TERM_BYTES == 8 #define TERM_BOXED_REFERENCE_RESOURCE_SIZE 4 +#else +// Enough size would be 4, but reference types +// are distinguished by size and 4 conflicts with +// TERM_BOXED_PROCESS_REF_SIZE on 32bit arch. +#define TERM_BOXED_REFERENCE_RESOURCE_SIZE 5 +#endif #define TERM_BOXED_REFERENCE_RESOURCE_HEADER (((TERM_BOXED_REFERENCE_RESOURCE_SIZE - 1) << 6) | TERM_BOXED_REF) #define TERM_BOXED_RESOURCE_SIZE TERM_BOXED_REFERENCE_RESOURCE_SIZE @@ -151,11 +158,11 @@ extern "C" { #define BOXED_INT64_SIZE (BOXED_TERMS_REQUIRED_FOR_INT64 + 1) #define BOXED_FUN_SIZE 3 #define FLOAT_SIZE (sizeof(float_term_t) / sizeof(term) + 1) +// Reference types are distinguished by their size. +// If you change a reference size, make sure it doesn't +// conflict with other reference sizes on all architectures. #define REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) -// FIXME: The required size is REF_SIZE + 1, but then it's equal to -// TERM_BOXED_REFERENCE_RESOURCE_SIZE on 32bit arch, and therefore -// the process ref is indistinguishable from resource ref there -#define TERM_BOXED_PROCESS_REF_SIZE 5 +#define TERM_BOXED_PROCESS_REF_SIZE REF_SIZE + 1 #define TERM_BOXED_PROCESS_REF_HEADER (((TERM_BOXED_PROCESS_REF_SIZE - 1) << 6) | TERM_BOXED_REF) #if TERM_BYTES == 8 #define EXTERNAL_PID_SIZE 3 @@ -2138,7 +2145,7 @@ static inline bool term_is_nomatch_binary_pos_len(BinaryPosLen pos_len) static inline BinaryPosLen term_nomatch_binary_pos_len(void) { - return (BinaryPosLen){ .pos = -1, .len = -1 }; + return (BinaryPosLen) { .pos = -1, .len = -1 }; } /** From b6eaf3698a6018e02e541b3d890b0570c546c86f Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 21 Jan 2026 11:14:16 +0100 Subject: [PATCH 06/23] Rename REF_SIZE -> SHORT_REF_SIZE Signed-off-by: Mateusz Front --- src/libAtomVM/ets.c | 2 +- src/libAtomVM/external_term.c | 2 +- src/libAtomVM/nifs.c | 4 +- src/libAtomVM/otp_socket.c | 12 ++--- src/libAtomVM/resources.h | 2 +- src/libAtomVM/term.h | 8 +-- .../emscripten/src/lib/websocket_nifs.c | 2 +- .../components/avm_builtins/adc_driver.c | 4 +- .../components/avm_builtins/i2c_resource.c | 54 ++++++++----------- 9 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/libAtomVM/ets.c b/src/libAtomVM/ets.c index 36f51e83bd..4316dc0ec2 100644 --- a/src/libAtomVM/ets.c +++ b/src/libAtomVM/ets.c @@ -178,7 +178,7 @@ EtsErrorCode ets_create_table_maybe_gc(term name, bool is_named, EtsTableType ta if (is_named) { *ret = name; } else { - if (UNLIKELY(memory_ensure_free_opt(ctx, REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, SHORT_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ets_hashtable_destroy(hashtable, ctx->global); free(ets_table); return EtsAllocationFailure; diff --git a/src/libAtomVM/external_term.c b/src/libAtomVM/external_term.c index ed9e2de3d4..a7e0c17706 100644 --- a/src/libAtomVM/external_term.c +++ b/src/libAtomVM/external_term.c @@ -1365,7 +1365,7 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini // Check if it's non-distributed node, in which case it's always a local ref if (external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) { if (len == 2) { - heap_size = REF_SIZE; + heap_size = SHORT_REF_SIZE; } else if (len == 4) { heap_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE; } diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index add76cbe37..2068a19434 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1785,7 +1785,7 @@ term nif_erlang_make_ref_0(Context *ctx, int argc, term argv[]) UNUSED(argv); // a ref is 64 bits, hence 8 bytes - if (UNLIKELY(memory_ensure_free_opt(ctx, REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, SHORT_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } @@ -4435,7 +4435,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) local_process_id = term_to_local_process_id(target_pid); // Monitoring self is possible but no monitor is actually created if (UNLIKELY(local_process_id == ctx->process_id)) { - if (UNLIKELY(memory_ensure_free_opt(ctx, REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, SHORT_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global); diff --git a/src/libAtomVM/otp_socket.c b/src/libAtomVM/otp_socket.c index 63e25919a8..7cf6630f26 100644 --- a/src/libAtomVM/otp_socket.c +++ b/src/libAtomVM/otp_socket.c @@ -246,7 +246,7 @@ static const AtomStringIntPair otp_socket_setopt_level_table[] = { static ErlNifResourceType *socket_resource_type; -#define SOCKET_MAKE_SELECT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + REF_SIZE + TUPLE_SIZE(2) + REF_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) +#define SOCKET_MAKE_SELECT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + SHORT_REF_SIZE + TUPLE_SIZE(2) + SHORT_REF_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) static term socket_make_select_notification(struct SocketResource *rsrc_obj, Heap *heap); // @@ -644,7 +644,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[]) term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj); enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + SHORT_REF_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); RAISE_ERROR(OUT_OF_MEMORY_ATOM); @@ -695,7 +695,7 @@ bool term_is_otp_socket(term socket_term) static int send_closed_notification(Context *ctx, term socket_term, int32_t selecting_process_id, struct SocketResource *rsrc_obj) { // send a {'$socket', Socket, abort, {Ref | undefined, closed}} message to the pid - if (UNLIKELY(memory_ensure_free_with_roots(ctx, TUPLE_SIZE(4) + TUPLE_SIZE(2) + REF_SIZE, 1, &socket_term, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_with_roots(ctx, TUPLE_SIZE(4) + TUPLE_SIZE(2) + SHORT_REF_SIZE, 1, &socket_term, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); return -1; } @@ -1902,7 +1902,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[]) term new_resource = enif_make_resource(erl_nif_env_from_context(ctx), conn_rsrc_obj); enif_release_resource(conn_rsrc_obj); // decrement refcount after enif_alloc_resource - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + SHORT_REF_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &new_resource, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); RAISE_ERROR(OUT_OF_MEMORY_ATOM); @@ -1933,7 +1933,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[]) SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - size_t requested_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE; + size_t requested_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + SHORT_REF_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); LWIP_END(); @@ -2311,7 +2311,7 @@ static term nif_socket_recv_lwip(Context *ctx, term resource_term, struct Socket } size_t ensure_packet_avail = term_binary_data_size_in_terms(len) + BINARY_HEADER_SIZE; - size_t requested_size = REF_SIZE + 2 * TUPLE_SIZE(2) + ensure_packet_avail + (is_recvfrom ? (TUPLE_SIZE(2) + INET_ADDR4_TUPLE_SIZE + TERM_MAP_SIZE(2)) : 0); + size_t requested_size = SHORT_REF_SIZE + 2 * TUPLE_SIZE(2) + ensure_packet_avail + (is_recvfrom ? (TUPLE_SIZE(2) + INET_ADDR4_TUPLE_SIZE + TERM_MAP_SIZE(2)) : 0); // Because resource is locked, we must ensure it's not garbage collected if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &resource_term, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); diff --git a/src/libAtomVM/resources.h b/src/libAtomVM/resources.h index 5c95a5c4ed..9e4e659e9c 100644 --- a/src/libAtomVM/resources.h +++ b/src/libAtomVM/resources.h @@ -164,7 +164,7 @@ void select_event_count_and_destroy_closed(struct ListHead *select_events, size_ */ void destroy_resource_monitors(struct RefcBinary *resource, GlobalContext *global); -#define SELECT_EVENT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + REF_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) +#define SELECT_EVENT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + SHORT_REF_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) /** * @brief Build a select event notification. diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 46491563d0..02577e325c 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -161,8 +161,8 @@ extern "C" { // Reference types are distinguished by their size. // If you change a reference size, make sure it doesn't // conflict with other reference sizes on all architectures. -#define REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) -#define TERM_BOXED_PROCESS_REF_SIZE REF_SIZE + 1 +#define SHORT_REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) +#define TERM_BOXED_PROCESS_REF_SIZE SHORT_REF_SIZE + 1 #define TERM_BOXED_PROCESS_REF_HEADER (((TERM_BOXED_PROCESS_REF_SIZE - 1) << 6) | TERM_BOXED_REF) #if TERM_BYTES == 8 #define EXTERNAL_PID_SIZE 3 @@ -2198,8 +2198,8 @@ static inline int term_bs_insert_binary(term t, int offset, term src, int n) */ static inline term term_from_ref_ticks(uint64_t ref_ticks, Heap *heap) { - term *boxed_value = memory_heap_alloc(heap, REF_SIZE); - boxed_value[0] = ((REF_SIZE - 1) << 6) | TERM_BOXED_REF; + term *boxed_value = memory_heap_alloc(heap, SHORT_REF_SIZE); + boxed_value[0] = ((SHORT_REF_SIZE - 1) << 6) | TERM_BOXED_REF; #if TERM_BYTES == 8 boxed_value[1] = (term) ref_ticks; diff --git a/src/platforms/emscripten/src/lib/websocket_nifs.c b/src/platforms/emscripten/src/lib/websocket_nifs.c index a6d0bc8c89..2d1280ffe8 100644 --- a/src/platforms/emscripten/src/lib/websocket_nifs.c +++ b/src/platforms/emscripten/src/lib/websocket_nifs.c @@ -95,7 +95,7 @@ static void websocket_down(ErlNifEnv *caller_env, void *obj, ErlNifPid *pid, Erl } } -#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + REF_SIZE + TUPLE_SIZE(3)) +#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + SHORT_REF_SIZE + TUPLE_SIZE(3)) static term term_make_websocket_resource(struct WebsocketResource *rsrc, Heap *heap) { diff --git a/src/platforms/esp32/components/avm_builtins/adc_driver.c b/src/platforms/esp32/components/avm_builtins/adc_driver.c index 67eaa6ac3b..c0dec82df3 100644 --- a/src/platforms/esp32/components/avm_builtins/adc_driver.c +++ b/src/platforms/esp32/components/avm_builtins/adc_driver.c @@ -352,7 +352,7 @@ static term nif_adc_init(Context *ctx, int argc, term argv[]) enif_release_resource(unit_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', Unit :: resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + SHORT_REF_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &unit_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); @@ -504,7 +504,7 @@ static term nif_adc_acquire(Context *ctx, int argc, term argv[]) enif_release_resource(chan_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + SHORT_REF_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &chan_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); diff --git a/src/platforms/esp32/components/avm_builtins/i2c_resource.c b/src/platforms/esp32/components/avm_builtins/i2c_resource.c index 246b8aa386..b5d2c632de 100644 --- a/src/platforms/esp32/components/avm_builtins/i2c_resource.c +++ b/src/platforms/esp32/components/avm_builtins/i2c_resource.c @@ -45,14 +45,14 @@ #define TAG "i2c_resource" -#define CHECK_ERROR(ctx, err, msg) \ -if (UNLIKELY(err != ESP_OK)) { \ - ESP_LOGE(TAG, msg ": err: %i.", err); \ - if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { \ - return OUT_OF_MEMORY_ATOM; \ - } \ - return create_error_tuple(ctx, esp_err_to_term(ctx->global, err)); \ -} +#define CHECK_ERROR(ctx, err, msg) \ + if (UNLIKELY(err != ESP_OK)) { \ + ESP_LOGE(TAG, msg ": err: %i.", err); \ + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { \ + return OUT_OF_MEMORY_ATOM; \ + } \ + return create_error_tuple(ctx, esp_err_to_term(ctx->global, err)); \ + } #define ACK_ENABLE true #define MS_TO_TICKS(MS) (MS / portTICK_PERIOD_MS) @@ -230,7 +230,7 @@ static term nif_i2c_open(Context *ctx, int argc, term argv[]) // // {'$i2c', Resource :: resource(), Ref :: reference()} :: i2c() - size_t requested_size = TUPLE_SIZE(3) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(3) + SHORT_REF_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { i2c_driver_delete(i2c_num); ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); @@ -308,8 +308,7 @@ static term nif_i2c_write_bytes(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid - ); + rsrc_obj->transmitting_pid); return create_error_tuple(ctx, reason); } @@ -414,8 +413,7 @@ static term nif_i2c_read_bytes(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid - ); + rsrc_obj->transmitting_pid); return create_error_tuple(ctx, reason); } @@ -521,8 +519,7 @@ static term nif_i2c_begin_transmission(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid - ); + rsrc_obj->transmitting_pid); return create_error_tuple(ctx, reason); } @@ -587,8 +584,7 @@ static term nif_i2c_enqueue_write_bytes(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid - ); + rsrc_obj->transmitting_pid); return create_error_tuple(ctx, reason); } @@ -640,8 +636,7 @@ static term nif_i2c_end_transmission(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid - ); + rsrc_obj->transmitting_pid); return create_error_tuple(ctx, reason); } @@ -682,38 +677,31 @@ static const ErlNifResourceTypeInit I2CResourceTypeInit = { .dtor = i2c_resource_dtor, }; -static const struct Nif i2c_open_nif = -{ +static const struct Nif i2c_open_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_i2c_open }; -static const struct Nif i2c_close_nif = -{ +static const struct Nif i2c_close_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_i2c_close }; -static const struct Nif i2c_read_bytes_nif = -{ +static const struct Nif i2c_read_bytes_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_i2c_read_bytes }; -static const struct Nif i2c_write_bytes_nif = -{ +static const struct Nif i2c_write_bytes_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_i2c_write_bytes }; -static const struct Nif i2c_begin_transmission_nif = -{ +static const struct Nif i2c_begin_transmission_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_i2c_begin_transmission }; -static const struct Nif i2c_enqueue_write_bytes_nif = -{ +static const struct Nif i2c_enqueue_write_bytes_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_i2c_enqueue_write_bytes }; -static const struct Nif i2c_end_transmission_nif = -{ +static const struct Nif i2c_end_transmission_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_i2c_end_transmission }; From af97dbdde18e18db295afdbe826273539b09a5bc Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 21 Jan 2026 11:27:00 +0100 Subject: [PATCH 07/23] CR Signed-off-by: Mateusz Front --- libs/estdlib/src/erlang.erl | 2 +- src/libAtomVM/term.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/estdlib/src/erlang.erl b/libs/estdlib/src/erlang.erl index ca1e985381..c39461174e 100644 --- a/libs/estdlib/src/erlang.erl +++ b/libs/estdlib/src/erlang.erl @@ -209,7 +209,7 @@ -type raise_stacktrace() :: [{module(), atom(), arity() | [term()]} | {function(), arity() | [term()]}] | stacktrace(). --type monitor_option() :: {'alias', 'explicit_unalias' | 'demonitor' | 'reply_demonitor'}. +-type monitor_option() :: {alias, explicit_unalias | demonitor | reply_demonitor}. %%----------------------------------------------------------------------------- %% @param Time time in milliseconds after which to send the timeout message. diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 02577e325c..3f695e4911 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -2145,7 +2145,7 @@ static inline bool term_is_nomatch_binary_pos_len(BinaryPosLen pos_len) static inline BinaryPosLen term_nomatch_binary_pos_len(void) { - return (BinaryPosLen) { .pos = -1, .len = -1 }; + return (BinaryPosLen){ .pos = -1, .len = -1 }; } /** From 8146397125265d11c19dc80696b5e74a9bc8bd63 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Tue, 27 Jan 2026 11:34:15 +0100 Subject: [PATCH 08/23] Support for external and resource refs in RefData Signed-off-by: Mateusz Front --- src/libAtomVM/nifs.c | 73 +++++++----- src/libAtomVM/term.h | 104 ++++++++++++++---- .../components/avm_builtins/socket_driver.c | 2 +- .../components/avm_builtins/uart_driver.c | 2 +- 4 files changed, 130 insertions(+), 51 deletions(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 2068a19434..acbcef059e 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1408,7 +1408,8 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free context_destroy(new_ctx); RAISE_ERROR(BADARG_ATOM); } - RefData ref_data = { .ref_ticks = 0, .process_id = 0 }; + RefData ref_data; + bool is_spawn_monitor = false; term new_pid = term_from_local_process_id(new_ctx->process_id); if (link_term == TRUE_ATOM) { @@ -1431,6 +1432,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free monitor_term = term_nil(); } if (term_is_list(monitor_term)) { + is_spawn_monitor = true; bool is_alias; enum ContextMonitorAliasType alias_type; @@ -1438,31 +1440,37 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free context_destroy(new_ctx); return term_invalid_term(); } - // We can call context_add_monitor directly on new process because it's not started yet ref_data.ref_ticks = globalcontext_get_ref_ticks(ctx->global); + struct Monitor *alias_monitor = NULL; + if (is_alias) { + ref_data = (RefData) { + .type = RefTypeProcess, + .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } + }; + alias_monitor = monitor_alias_new(ref_data, alias_type); + if (IS_NULL_PTR(alias_monitor)) { + context_destroy(new_ctx); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + } else { + ref_data = (RefData) { .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + } struct Monitor *new_monitor = monitor_new(term_from_local_process_id(ctx->process_id), ref_data, false); if (IS_NULL_PTR(new_monitor)) { context_destroy(new_ctx); + free(alias_monitor); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } struct Monitor *self_monitor = monitor_new(new_pid, ref_data, true); if (IS_NULL_PTR(self_monitor)) { + free(alias_monitor); free(new_monitor); context_destroy(new_ctx); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - struct Monitor *alias_monitor = NULL; - if (is_alias) { - ref_data.process_id = ctx->process_id; - alias_monitor = monitor_alias_new(ref_data, alias_type); - if (IS_NULL_PTR(alias_monitor)) { - free(new_monitor); - free(self_monitor); - context_destroy(new_ctx); - RAISE_ERROR(OUT_OF_MEMORY_ATOM); - } - } + + // We can call context_add_monitor directly on new process because it's not started yet context_add_monitor(new_ctx, new_monitor); context_add_monitor(ctx, self_monitor); if (is_alias) { @@ -1470,7 +1478,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free } } - if (ref_data.ref_ticks) { + if (is_spawn_monitor) { int res_size = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free_opt(ctx, res_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { context_destroy(new_ctx); @@ -4473,8 +4481,22 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) if ((object_type == PROCESS_ATOM && target->native_handler != NULL) || (object_type == PORT_ATOM && target->native_handler == NULL)) { RAISE_ERROR(BADARG_ATOM); } - RefData ref_data = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = 0 }; - term monitoring_pid = term_from_local_process_id(ctx->process_id); + + RefData ref_data; + struct Monitor *alias_monitor = NULL; + if (is_alias) { + ref_data = (RefData) { + .type = RefTypeProcess, + .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } + }; + alias_monitor = monitor_alias_new(ref_data, alias_type); + if (IS_NULL_PTR(alias_monitor)) { + globalcontext_get_process_unlock(ctx->global, target); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + } else { + ref_data = (RefData) { .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + } struct Monitor *self_monitor; if (term_is_atom(target_proc)) { self_monitor = monitor_registeredname_monitor_new(local_process_id, target_proc, ref_data); @@ -4483,25 +4505,17 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) } if (IS_NULL_PTR(self_monitor)) { globalcontext_get_process_unlock(ctx->global, target); + free(alias_monitor); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } + term monitoring_pid = term_from_local_process_id(ctx->process_id); struct Monitor *other_monitor = monitor_new(monitoring_pid, ref_data, false); if (IS_NULL_PTR(other_monitor)) { + free(alias_monitor); free(self_monitor); globalcontext_get_process_unlock(ctx->global, target); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - struct Monitor *alias_monitor = NULL; - if (is_alias) { - ref_data.process_id = ctx->process_id; - alias_monitor = monitor_alias_new(ref_data, alias_type); - if (IS_NULL_PTR(alias_monitor)) { - free(self_monitor); - free(other_monitor); - globalcontext_get_process_unlock(ctx->global, target); - RAISE_ERROR(OUT_OF_MEMORY_ATOM); - } - } mailbox_send_monitor_signal(target, MonitorSignal, other_monitor); globalcontext_get_process_unlock(ctx->global, target); @@ -6681,7 +6695,10 @@ static term nif_erlang_alias(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - RefData ref_data = { .process_id = ctx->process_id, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + RefData ref_data = { + .type = RefTypeProcess, + .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } + }; term process_ref = term_from_ref_data(ref_data, &ctx->heap); struct Monitor *monitor = monitor_alias_new(ref_data, ContextMonitorAliasExplicitUnalias); if (IS_NULL_PTR(monitor)) { diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 3f695e4911..05337529b3 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -258,14 +258,41 @@ extern "C" { typedef struct GlobalContext GlobalContext; #endif -typedef struct RefData RefData; +enum RefType +{ + RefTypeShort, + RefTypeProcess, + RefTypeResource, + RefTypeExternal +}; -struct RefData +struct ProcessRefData { uint64_t ref_ticks; int32_t process_id; }; +struct ExternalRefData +{ + term node; + uint32_t creation; + uint16_t len; + const uint32_t *words; +}; + +typedef struct RefData RefData; +struct RefData +{ + enum RefType type; + union + { + uint64_t ref_ticks; + struct ProcessRefData process; + void *resource; + struct ExternalRefData external; + }; +}; + typedef struct PrinterFun PrinterFun; typedef int (*printer_function_t)(PrinterFun *fun, const char *fmt, ...) PRINTF_FORMAT_ARGS(2, 3); @@ -2145,7 +2172,7 @@ static inline bool term_is_nomatch_binary_pos_len(BinaryPosLen pos_len) static inline BinaryPosLen term_nomatch_binary_pos_len(void) { - return (BinaryPosLen){ .pos = -1, .len = -1 }; + return (BinaryPosLen) { .pos = -1, .len = -1 }; } /** @@ -2276,23 +2303,6 @@ static inline uint32_t term_process_ref_to_process_id(term rt) #endif } -static inline RefData term_to_ref_data(term t) -{ - RefData ref_data; - ref_data.ref_ticks = term_to_ref_ticks(t); - ref_data.process_id = term_is_process_reference(t) ? term_process_ref_to_process_id(t) : 0; - return ref_data; -} - -static inline term term_from_ref_data(RefData ref_data, Heap *heap) -{ - if (ref_data.process_id) { - return term_make_process_reference(ref_data.process_id, ref_data.ref_ticks, heap); - } else { - return term_from_ref_ticks(ref_data.ref_ticks, heap); - } -} - /** * @brief Make an external pid term from node, process_id, serial and creation * @@ -2429,7 +2439,7 @@ static inline uint64_t term_get_external_port_number(term t) * @param heap the heap to allocate memory in * @return an external heap term created using given parameters. */ -static inline term term_make_external_reference(term node, uint16_t len, uint32_t *data, uint32_t creation, Heap *heap) +static inline term term_make_external_reference(term node, uint16_t len, const uint32_t *data, uint32_t creation, Heap *heap) { TERM_DEBUG_ASSERT(term_is_atom(node)); @@ -3098,6 +3108,58 @@ static inline term term_from_resource(void *resource, Heap *heap) return ret; } +static inline RefData term_to_ref_data(term t) +{ + TERM_DEBUG_ASSERT(term_is_reference(t)); + + RefData ref_data; + if (term_is_external_reference(t)) { + ref_data.type = RefTypeExternal; + ref_data.external.node = term_get_external_node(t); + ref_data.external.creation = term_get_external_node_creation(t); + ref_data.external.len = term_get_external_reference_len(t); + ref_data.external.words = term_get_external_reference_words(t); + } else if (term_is_process_reference(t)) { + ref_data.type = RefTypeProcess; + ref_data.process.ref_ticks = term_to_ref_ticks(t); + ref_data.process.process_id = term_process_ref_to_process_id(t); + } else if (term_is_resource_reference(t)) { + ref_data.type = RefTypeResource; + ref_data.resource = &term_resource_refc_binary_ptr(t)->data; + } else { + ref_data.type = RefTypeShort; + ref_data.ref_ticks = term_to_ref_ticks(t); + } + + return ref_data; +} + +static inline term term_from_ref_data(RefData ref_data, Heap *heap) +{ + switch (ref_data.type) { + case RefTypeShort: { + return term_from_ref_ticks(ref_data.ref_ticks, heap); + } + case RefTypeProcess: { + return term_make_process_reference(ref_data.process.process_id, ref_data.ref_ticks, heap); + } + case RefTypeResource: { + return term_from_resource(ref_data.resource, heap); + } + case RefTypeExternal: { + return term_make_external_reference( + ref_data.external.node, + ref_data.external.len, + ref_data.external.words, + ref_data.external.creation, + heap); + } + default: { + UNREACHABLE(); + } + } +} + /** * @brief Get a resource term from a resource type and a serialization reference * number. diff --git a/src/platforms/esp32/components/avm_builtins/socket_driver.c b/src/platforms/esp32/components/avm_builtins/socket_driver.c index 2ac47d9520..8769a4ff75 100644 --- a/src/platforms/esp32/components/avm_builtins/socket_driver.c +++ b/src/platforms/esp32/components/avm_builtins/socket_driver.c @@ -79,7 +79,7 @@ static const char *gen_udp_moniker_atom = ATOM_STR("\xC", "$avm_gen_udp"); static const char *native_udp_module_atom = ATOM_STR("\xC", "gen_udp_inet"); static const RefData no_ref_data = { - .process_id = 0, + .type = RefTypeShort, .ref_ticks = 0, }; diff --git a/src/platforms/esp32/components/avm_builtins/uart_driver.c b/src/platforms/esp32/components/avm_builtins/uart_driver.c index c7927668d3..20e4edf902 100644 --- a/src/platforms/esp32/components/avm_builtins/uart_driver.c +++ b/src/platforms/esp32/components/avm_builtins/uart_driver.c @@ -64,7 +64,7 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx); #endif static const RefData no_ref_data = { - .process_id = 0, + .type = RefTypeShort, .ref_ticks = 0, }; From d262e75d67a224a6f13293ea2abc6dc57e5de43d Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Tue, 27 Jan 2026 17:32:17 +0100 Subject: [PATCH 09/23] CR fixes Signed-off-by: Mateusz Front --- src/libAtomVM/context.c | 5 +++-- src/libAtomVM/jit.c | 8 +------- src/libAtomVM/nifs.c | 6 +++--- src/libAtomVM/term.h | 12 +++++++++++- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index b0c5a49cca..98f3e72049 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -806,6 +806,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) } case CONTEXT_MONITOR_ALIAS: { free(monitor); + break; } } } @@ -1309,8 +1310,8 @@ COLD_FUNC void context_dump(Context *ctx) break; } case CONTEXT_MONITOR_ALIAS: { - struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor); - fprintf(stderr, "has alias ref=%lu", (long unsigned) monitored_monitor->ref_data.ref_ticks); + struct MonitorAlias *monitor_alias = CONTAINER_OF(monitor, struct MonitorAlias, monitor); + fprintf(stderr, "has alias ref=%lu", (long unsigned) monitor_alias->ref_data.ref_ticks); fprintf(stderr, "\n"); break; } diff --git a/src/libAtomVM/jit.c b/src/libAtomVM/jit.c index 386871d981..2dcdbf538e 100644 --- a/src/libAtomVM/jit.c +++ b/src/libAtomVM/jit.c @@ -759,13 +759,7 @@ static bool jit_send(Context *ctx, JITState *jit_state) } ctx->x[0] = return_value; } else if (term_is_local_pid_or_port(recipient_term)) { - int local_process_id; - if (term_is_local_pid_or_port(recipient_term)) { - local_process_id = term_to_local_process_id(recipient_term); - } else { - set_error(ctx, jit_state, 0, BADARG_ATOM); - return false; - } + int local_process_id = term_to_local_process_id(recipient_term); globalcontext_send_message(ctx->global, local_process_id, ctx->x[1]); ctx->x[0] = ctx->x[1]; } else if (term_is_atom(recipient_term)) { diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index acbcef059e..36c8c2dd65 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1203,7 +1203,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) { // msg is not in the port's heap NativeHandlerResult result = NativeContinue; - if (UNLIKELY(memory_ensure_free_opt(ctx, 13, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(3) + TERM_BOXED_PROCESS_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { fprintf(stderr, "Unable to allocate sufficient memory for console driver.\n"); AVM_ABORT(); } @@ -1300,7 +1300,7 @@ static term parse_monitor_opts(Context *ctx, term monitor_opts, bool *is_alias, *is_alias = false; while (term_is_nonempty_list(monitor_opts)) { term option = term_get_list_head(monitor_opts); - if (term_is_tuple(option) && term_get_tuple_element(option, 0) == ALIAS_ATOM) { + if (term_is_tuple(option) && term_get_tuple_arity(option) == 2 && term_get_tuple_element(option, 0) == ALIAS_ATOM) { *is_alias = true; switch (term_get_tuple_element(option, 1)) { case EXPLICIT_UNALIAS_ATOM: @@ -1315,7 +1315,7 @@ static term parse_monitor_opts(Context *ctx, term monitor_opts, bool *is_alias, default: RAISE_ERROR(BADARG_ATOM); } - } else if (term_is_tuple(option) && term_get_tuple_element(option, 0) == TAG_ATOM) { + } else if (term_is_tuple(option) && term_get_tuple_arity(option) == 2 && term_get_tuple_element(option, 0) == TAG_ATOM) { RAISE_ERROR(UNSUPPORTED_ATOM); } else { RAISE_ERROR(BADARG_ATOM); diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 05337529b3..ab9c419ac2 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -162,8 +162,10 @@ extern "C" { // If you change a reference size, make sure it doesn't // conflict with other reference sizes on all architectures. #define SHORT_REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) -#define TERM_BOXED_PROCESS_REF_SIZE SHORT_REF_SIZE + 1 +#define TERM_BOXED_PROCESS_REF_SIZE (SHORT_REF_SIZE + 1) #define TERM_BOXED_PROCESS_REF_HEADER (((TERM_BOXED_PROCESS_REF_SIZE - 1) << 6) | TERM_BOXED_REF) +_Static_assert(SHORT_REF_SIZE < TERM_BOXED_PROCESS_REF_SIZE); +_Static_assert(TERM_BOXED_PROCESS_REF_SIZE < TERM_BOXED_REFERENCE_RESOURCE_SIZE); #if TERM_BYTES == 8 #define EXTERNAL_PID_SIZE 3 #elif TERM_BYTES == 4 @@ -183,6 +185,14 @@ extern "C" { #define CONS_SIZE 2 #define REFC_BINARY_CONS_OFFSET 4 #define REFERENCE_RESOURCE_CONS_OFFSET 2 + +#if TERM_BYTES == 4 +#define REFERENCE_PROCESS_PID_OFFSET 2 + +#elif TERM_BYTES == 8 +#define REFERENCE_PROCESS_PID_OFFSET 2 +#endif + #define LIST_SIZE(num_elements, element_size) ((num_elements) * ((element_size) + CONS_SIZE)) #define TERM_STRING_SIZE(length) (2 * (length)) #define TERM_MAP_SIZE(num_elements) (3 + 2 * (num_elements)) From 6a9730d55acaa4b402e4fc3d79c7016b287a9b14 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Tue, 27 Jan 2026 17:54:00 +0100 Subject: [PATCH 10/23] rename ref sizes Signed-off-by: Mateusz Front --- src/libAtomVM/context.c | 2 +- src/libAtomVM/ets.c | 2 +- src/libAtomVM/external_term.c | 2 +- src/libAtomVM/nifs.c | 14 +++++----- src/libAtomVM/otp_socket.c | 12 ++++----- src/libAtomVM/resources.h | 2 +- src/libAtomVM/term.h | 26 +++++++++++-------- .../emscripten/src/lib/websocket_nifs.c | 2 +- .../components/avm_builtins/adc_driver.c | 4 +-- .../components/avm_builtins/i2c_resource.c | 2 +- .../components/avm_builtins/network_driver.c | 6 ++--- .../components/avm_builtins/socket_driver.c | 2 +- .../components/avm_builtins/uart_driver.c | 4 +-- src/platforms/rp2/src/lib/networkdriver.c | 6 ++--- 14 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index 98f3e72049..4d07ee1bdc 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -779,7 +779,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) Context *target = globalcontext_get_process_nolock(glb, local_process_id); // Target cannot be NULL as we processed Demonitor signals assert(target != NULL); - int required_terms = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(5); + int required_terms = TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(5); if (UNLIKELY(memory_ensure_free(ctx, required_terms) != MEMORY_GC_OK)) { // TODO: handle out of memory here fprintf(stderr, "Cannot handle out of memory.\n"); diff --git a/src/libAtomVM/ets.c b/src/libAtomVM/ets.c index 4316dc0ec2..c65b2923c1 100644 --- a/src/libAtomVM/ets.c +++ b/src/libAtomVM/ets.c @@ -178,7 +178,7 @@ EtsErrorCode ets_create_table_maybe_gc(term name, bool is_named, EtsTableType ta if (is_named) { *ret = name; } else { - if (UNLIKELY(memory_ensure_free_opt(ctx, SHORT_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_REFERENCE_SHORT_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ets_hashtable_destroy(hashtable, ctx->global); free(ets_table); return EtsAllocationFailure; diff --git a/src/libAtomVM/external_term.c b/src/libAtomVM/external_term.c index a7e0c17706..8f0e92c18e 100644 --- a/src/libAtomVM/external_term.c +++ b/src/libAtomVM/external_term.c @@ -1365,7 +1365,7 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini // Check if it's non-distributed node, in which case it's always a local ref if (external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) { if (len == 2) { - heap_size = SHORT_REF_SIZE; + heap_size = TERM_BOXED_REFERENCE_SHORT_SIZE; } else if (len == 4) { heap_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE; } diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 36c8c2dd65..6c1a4f6401 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1203,7 +1203,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) { // msg is not in the port's heap NativeHandlerResult result = NativeContinue; - if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(3) + TERM_BOXED_PROCESS_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_PROCESS_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { fprintf(stderr, "Unable to allocate sufficient memory for console driver.\n"); AVM_ABORT(); } @@ -1479,7 +1479,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free } if (is_spawn_monitor) { - int res_size = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); + int res_size = TERM_BOXED_REFERENCE_PROCESS_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); @@ -1793,7 +1793,7 @@ term nif_erlang_make_ref_0(Context *ctx, int argc, term argv[]) UNUSED(argv); // a ref is 64 bits, hence 8 bytes - if (UNLIKELY(memory_ensure_free_opt(ctx, SHORT_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_REFERENCE_SHORT_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } @@ -4443,7 +4443,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) local_process_id = term_to_local_process_id(target_pid); // Monitoring self is possible but no monitor is actually created if (UNLIKELY(local_process_id == ctx->process_id)) { - if (UNLIKELY(memory_ensure_free_opt(ctx, SHORT_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_REFERENCE_SHORT_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global); @@ -4455,7 +4455,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) } if (IS_NULL_PTR(target)) { - int res_size = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(5) + target_proc_size; + int res_size = TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(5) + target_proc_size; if (UNLIKELY(memory_ensure_free_opt(ctx, res_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } @@ -4524,7 +4524,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) context_add_monitor(ctx, alias_monitor); } - if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_PROCESS_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_REFERENCE_PROCESS_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } @@ -6691,7 +6691,7 @@ static term nif_erlang_alias(Context *ctx, int argc, term argv[]) UNUSED(argc); UNUSED(argv); - if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_PROCESS_REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_REFERENCE_PROCESS_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } diff --git a/src/libAtomVM/otp_socket.c b/src/libAtomVM/otp_socket.c index 7cf6630f26..bb8778c2e5 100644 --- a/src/libAtomVM/otp_socket.c +++ b/src/libAtomVM/otp_socket.c @@ -246,7 +246,7 @@ static const AtomStringIntPair otp_socket_setopt_level_table[] = { static ErlNifResourceType *socket_resource_type; -#define SOCKET_MAKE_SELECT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + SHORT_REF_SIZE + TUPLE_SIZE(2) + SHORT_REF_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) +#define SOCKET_MAKE_SELECT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) static term socket_make_select_notification(struct SocketResource *rsrc_obj, Heap *heap); // @@ -644,7 +644,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[]) term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj); enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + SHORT_REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); RAISE_ERROR(OUT_OF_MEMORY_ATOM); @@ -695,7 +695,7 @@ bool term_is_otp_socket(term socket_term) static int send_closed_notification(Context *ctx, term socket_term, int32_t selecting_process_id, struct SocketResource *rsrc_obj) { // send a {'$socket', Socket, abort, {Ref | undefined, closed}} message to the pid - if (UNLIKELY(memory_ensure_free_with_roots(ctx, TUPLE_SIZE(4) + TUPLE_SIZE(2) + SHORT_REF_SIZE, 1, &socket_term, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_with_roots(ctx, TUPLE_SIZE(4) + TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE, 1, &socket_term, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); return -1; } @@ -1902,7 +1902,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[]) term new_resource = enif_make_resource(erl_nif_env_from_context(ctx), conn_rsrc_obj); enif_release_resource(conn_rsrc_obj); // decrement refcount after enif_alloc_resource - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + SHORT_REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &new_resource, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); RAISE_ERROR(OUT_OF_MEMORY_ATOM); @@ -1933,7 +1933,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[]) SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - size_t requested_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + SHORT_REF_SIZE; + size_t requested_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); LWIP_END(); @@ -2311,7 +2311,7 @@ static term nif_socket_recv_lwip(Context *ctx, term resource_term, struct Socket } size_t ensure_packet_avail = term_binary_data_size_in_terms(len) + BINARY_HEADER_SIZE; - size_t requested_size = SHORT_REF_SIZE + 2 * TUPLE_SIZE(2) + ensure_packet_avail + (is_recvfrom ? (TUPLE_SIZE(2) + INET_ADDR4_TUPLE_SIZE + TERM_MAP_SIZE(2)) : 0); + size_t requested_size = TERM_BOXED_REFERENCE_SHORT_SIZE + 2 * TUPLE_SIZE(2) + ensure_packet_avail + (is_recvfrom ? (TUPLE_SIZE(2) + INET_ADDR4_TUPLE_SIZE + TERM_MAP_SIZE(2)) : 0); // Because resource is locked, we must ensure it's not garbage collected if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &resource_term, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); diff --git a/src/libAtomVM/resources.h b/src/libAtomVM/resources.h index 9e4e659e9c..58c97cf9b6 100644 --- a/src/libAtomVM/resources.h +++ b/src/libAtomVM/resources.h @@ -164,7 +164,7 @@ void select_event_count_and_destroy_closed(struct ListHead *select_events, size_ */ void destroy_resource_monitors(struct RefcBinary *resource, GlobalContext *global); -#define SELECT_EVENT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + SHORT_REF_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) +#define SELECT_EVENT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + TERM_BOXED_REFERENCE_SHORT_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE) /** * @brief Build a select event notification. diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index ab9c419ac2..0d9c1c2122 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -134,7 +134,7 @@ extern "C" { #else // Enough size would be 4, but reference types // are distinguished by size and 4 conflicts with -// TERM_BOXED_PROCESS_REF_SIZE on 32bit arch. +// TERM_BOXED_REFERENCE_PROCESS_SIZE on 32bit arch. #define TERM_BOXED_REFERENCE_RESOURCE_SIZE 5 #endif #define TERM_BOXED_REFERENCE_RESOURCE_HEADER (((TERM_BOXED_REFERENCE_RESOURCE_SIZE - 1) << 6) | TERM_BOXED_REF) @@ -161,11 +161,10 @@ extern "C" { // Reference types are distinguished by their size. // If you change a reference size, make sure it doesn't // conflict with other reference sizes on all architectures. -#define SHORT_REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) -#define TERM_BOXED_PROCESS_REF_SIZE (SHORT_REF_SIZE + 1) -#define TERM_BOXED_PROCESS_REF_HEADER (((TERM_BOXED_PROCESS_REF_SIZE - 1) << 6) | TERM_BOXED_REF) -_Static_assert(SHORT_REF_SIZE < TERM_BOXED_PROCESS_REF_SIZE); -_Static_assert(TERM_BOXED_PROCESS_REF_SIZE < TERM_BOXED_REFERENCE_RESOURCE_SIZE); +#define TERM_BOXED_REFERENCE_SHORT_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) +#define REF_SIZE _Pragma("REF_SIZE is deprecated, use TERM_BOXED_REFERENCE_SHORT_SIZE instead") TERM_BOXED_REFERENCE_SHORT_SIZE +#define TERM_BOXED_REFERENCE_PROCESS_SIZE (TERM_BOXED_REFERENCE_SHORT_SIZE + 1) +#define TERM_BOXED_REFERENCE_PROCESS_HEADER (((TERM_BOXED_REFERENCE_PROCESS_SIZE - 1) << 6) | TERM_BOXED_REF) #if TERM_BYTES == 8 #define EXTERNAL_PID_SIZE 3 #elif TERM_BYTES == 4 @@ -181,6 +180,11 @@ _Static_assert(TERM_BOXED_PROCESS_REF_SIZE < TERM_BOXED_REFERENCE_RESOURCE_SIZE) #else #error #endif +#define EXTERNAL_REF_MAX_WORDS 5 +#define TERM_BOXED_REFERENCE_MAX_SIZE EXTERNAL_REF_SIZE(EXTERNAL_REF_MAX_WORDS) +_Static_assert(TERM_BOXED_REFERENCE_SHORT_SIZE < TERM_BOXED_REFERENCE_PROCESS_SIZE, "Short ref size must be smaller than process ref size"); +_Static_assert(TERM_BOXED_REFERENCE_PROCESS_SIZE < TERM_BOXED_REFERENCE_RESOURCE_SIZE, "Process ref size must be smaller than reference resource size"); +_Static_assert(TERM_BOXED_REFERENCE_PROCESS_SIZE <= TERM_BOXED_REFERENCE_MAX_SIZE, "Max ref size can't be smaller than all other ref sizes"); #define TUPLE_SIZE(elems) ((int) (elems + 1)) #define CONS_SIZE 2 #define REFC_BINARY_CONS_OFFSET 4 @@ -948,7 +952,7 @@ static inline bool term_is_process_reference(term t) { if (term_is_boxed(t)) { const term *boxed_value = term_to_const_term_ptr(t); - if (boxed_value[0] == TERM_BOXED_PROCESS_REF_HEADER) { + if (boxed_value[0] == TERM_BOXED_REFERENCE_PROCESS_HEADER) { return true; } } @@ -2235,8 +2239,8 @@ static inline int term_bs_insert_binary(term t, int offset, term src, int n) */ static inline term term_from_ref_ticks(uint64_t ref_ticks, Heap *heap) { - term *boxed_value = memory_heap_alloc(heap, SHORT_REF_SIZE); - boxed_value[0] = ((SHORT_REF_SIZE - 1) << 6) | TERM_BOXED_REF; + term *boxed_value = memory_heap_alloc(heap, TERM_BOXED_REFERENCE_SHORT_SIZE); + boxed_value[0] = ((TERM_BOXED_REFERENCE_SHORT_SIZE - 1) << 6) | TERM_BOXED_REF; #if TERM_BYTES == 8 boxed_value[1] = (term) ref_ticks; @@ -2281,8 +2285,8 @@ static inline uint64_t term_to_ref_ticks(term rt) */ static inline term term_make_process_reference(int32_t process_id, uint64_t ref_ticks, Heap *heap) { - term *boxed_value = memory_heap_alloc(heap, TERM_BOXED_PROCESS_REF_SIZE); - boxed_value[0] = TERM_BOXED_PROCESS_REF_HEADER; + term *boxed_value = memory_heap_alloc(heap, TERM_BOXED_REFERENCE_PROCESS_SIZE); + boxed_value[0] = TERM_BOXED_REFERENCE_PROCESS_HEADER; #if TERM_BYTES == 4 boxed_value[1] = (ref_ticks >> 32); diff --git a/src/platforms/emscripten/src/lib/websocket_nifs.c b/src/platforms/emscripten/src/lib/websocket_nifs.c index 2d1280ffe8..ab8e084652 100644 --- a/src/platforms/emscripten/src/lib/websocket_nifs.c +++ b/src/platforms/emscripten/src/lib/websocket_nifs.c @@ -95,7 +95,7 @@ static void websocket_down(ErlNifEnv *caller_env, void *obj, ErlNifPid *pid, Erl } } -#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + SHORT_REF_SIZE + TUPLE_SIZE(3)) +#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(3)) static term term_make_websocket_resource(struct WebsocketResource *rsrc, Heap *heap) { diff --git a/src/platforms/esp32/components/avm_builtins/adc_driver.c b/src/platforms/esp32/components/avm_builtins/adc_driver.c index c0dec82df3..3a7392e401 100644 --- a/src/platforms/esp32/components/avm_builtins/adc_driver.c +++ b/src/platforms/esp32/components/avm_builtins/adc_driver.c @@ -352,7 +352,7 @@ static term nif_adc_init(Context *ctx, int argc, term argv[]) enif_release_resource(unit_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', Unit :: resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + SHORT_REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &unit_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); @@ -504,7 +504,7 @@ static term nif_adc_acquire(Context *ctx, int argc, term argv[]) enif_release_resource(chan_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + SHORT_REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &chan_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); diff --git a/src/platforms/esp32/components/avm_builtins/i2c_resource.c b/src/platforms/esp32/components/avm_builtins/i2c_resource.c index b5d2c632de..d4d9caf8db 100644 --- a/src/platforms/esp32/components/avm_builtins/i2c_resource.c +++ b/src/platforms/esp32/components/avm_builtins/i2c_resource.c @@ -230,7 +230,7 @@ static term nif_i2c_open(Context *ctx, int argc, term argv[]) // // {'$i2c', Resource :: resource(), Ref :: reference()} :: i2c() - size_t requested_size = TUPLE_SIZE(3) + SHORT_REF_SIZE; + size_t requested_size = TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { i2c_driver_delete(i2c_num); ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); diff --git a/src/platforms/esp32/components/avm_builtins/network_driver.c b/src/platforms/esp32/components/avm_builtins/network_driver.c index 82c8cc1dbb..484d39c273 100644 --- a/src/platforms/esp32/components/avm_builtins/network_driver.c +++ b/src/platforms/esp32/components/avm_builtins/network_driver.c @@ -58,7 +58,7 @@ #define TCPIP_HOSTNAME_MAX_SIZE 255 #define TAG "network_driver" -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE) static const char *const ap_atom = ATOM_STR("\x2", "ap"); static const char *const ap_channel_atom = ATOM_STR("\xA", "ap_channel"); @@ -1086,7 +1086,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { ESP_LOGE(TAG, "Unrecognized command: %x", cmd); // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; @@ -1096,7 +1096,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; diff --git a/src/platforms/esp32/components/avm_builtins/socket_driver.c b/src/platforms/esp32/components/avm_builtins/socket_driver.c index 8769a4ff75..10b141ed75 100644 --- a/src/platforms/esp32/components/avm_builtins/socket_driver.c +++ b/src/platforms/esp32/components/avm_builtins/socket_driver.c @@ -443,7 +443,7 @@ static struct UDPSocketData *udp_socket_data_new(Context *ctx, struct netconn *c } // When this method is called, ensure free was called with REPLY_SIZE -#define REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE) +#define REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE) static void do_send_reply(Context *ctx, term reply, RefData ref_data, int32_t pid) { GlobalContext *glb = ctx->global; diff --git a/src/platforms/esp32/components/avm_builtins/uart_driver.c b/src/platforms/esp32/components/avm_builtins/uart_driver.c index 20e4edf902..3d991d0bee 100644 --- a/src/platforms/esp32/components/avm_builtins/uart_driver.c +++ b/src/platforms/esp32/components/avm_builtins/uart_driver.c @@ -132,7 +132,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen int bin_size = term_binary_heap_size(event.size); Heap heap; - if (UNLIKELY(memory_init_heap(&heap, bin_size + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_init_heap(&heap, bin_size + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); AVM_ABORT(); } @@ -515,7 +515,7 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx) int local_pid = term_to_local_process_id(gen_message.pid); if (is_closed) { - if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + TERM_BOXED_PROCESS_REF_SIZE) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + TERM_BOXED_REFERENCE_PROCESS_SIZE) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "[uart_driver_consume_mailbox] Failed to allocate space for error tuple"); globalcontext_send_message(glb, local_pid, OUT_OF_MEMORY_ATOM); } diff --git a/src/platforms/rp2/src/lib/networkdriver.c b/src/platforms/rp2/src/lib/networkdriver.c index a097eed582..0f0285cb99 100644 --- a/src/platforms/rp2/src/lib/networkdriver.c +++ b/src/platforms/rp2/src/lib/networkdriver.c @@ -41,7 +41,7 @@ #pragma GCC diagnostic pop -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE) #define DEFAULT_HOSTNAME_FMT "atomvm-%02x%02x%02x%02x%02x%02x" #define DEFAULT_HOSTNAME_SIZE (strlen("atomvm-") + 12 + 1) @@ -759,7 +759,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } @@ -768,7 +768,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } From 628ce83a4224c39e08f99bfd6ae421135c5bdf60 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 28 Jan 2026 16:24:36 +0100 Subject: [PATCH 11/23] fix formatting Signed-off-by: Mateusz Front --- src/libAtomVM/nifs.c | 4 ++-- src/libAtomVM/term.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 6c1a4f6401..1989a4a110 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1453,7 +1453,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free RAISE_ERROR(OUT_OF_MEMORY_ATOM); } } else { - ref_data = (RefData) { .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + ref_data = (RefData){ .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; } struct Monitor *new_monitor = monitor_new(term_from_local_process_id(ctx->process_id), ref_data, false); @@ -4495,7 +4495,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } } else { - ref_data = (RefData) { .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + ref_data = (RefData){ .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; } struct Monitor *self_monitor; if (term_is_atom(target_proc)) { diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 0d9c1c2122..2e4c9c7a55 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -2186,7 +2186,7 @@ static inline bool term_is_nomatch_binary_pos_len(BinaryPosLen pos_len) static inline BinaryPosLen term_nomatch_binary_pos_len(void) { - return (BinaryPosLen) { .pos = -1, .len = -1 }; + return (BinaryPosLen){ .pos = -1, .len = -1 }; } /** From 8c5fd6435821f512baf71360959dbc39e646b115 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Mon, 2 Feb 2026 14:03:14 +0100 Subject: [PATCH 12/23] CR Signed-off-by: Mateusz Front --- src/libAtomVM/nifs.c | 4 ++-- src/libAtomVM/term.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 1989a4a110..60f6c6005a 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1443,7 +1443,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free ref_data.ref_ticks = globalcontext_get_ref_ticks(ctx->global); struct Monitor *alias_monitor = NULL; if (is_alias) { - ref_data = (RefData) { + ref_data = (RefData){ .type = RefTypeProcess, .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } }; @@ -4485,7 +4485,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RefData ref_data; struct Monitor *alias_monitor = NULL; if (is_alias) { - ref_data = (RefData) { + ref_data = (RefData){ .type = RefTypeProcess, .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } }; diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 2e4c9c7a55..f511af0a00 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -3126,7 +3126,7 @@ static inline RefData term_to_ref_data(term t) { TERM_DEBUG_ASSERT(term_is_reference(t)); - RefData ref_data; + RefData ref_data = { 0 }; // Needed for GCC 10 if (term_is_external_reference(t)) { ref_data.type = RefTypeExternal; ref_data.external.node = term_get_external_node(t); From caa4fe2181a561555937f0048590789722493592 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Tue, 3 Feb 2026 17:27:54 +0100 Subject: [PATCH 13/23] fix reference_process_pid_offset Signed-off-by: Mateusz Front --- src/libAtomVM/term.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index f511af0a00..87534e0f92 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -194,7 +194,7 @@ _Static_assert(TERM_BOXED_REFERENCE_PROCESS_SIZE <= TERM_BOXED_REFERENCE_MAX_SIZ #define REFERENCE_PROCESS_PID_OFFSET 2 #elif TERM_BYTES == 8 -#define REFERENCE_PROCESS_PID_OFFSET 2 +#define REFERENCE_PROCESS_PID_OFFSET 1 #endif #define LIST_SIZE(num_elements, element_size) ((num_elements) * ((element_size) + CONS_SIZE)) From eaf97a720f1f2b5901213d20876c47877a68297f Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Tue, 3 Feb 2026 17:33:55 +0100 Subject: [PATCH 14/23] fix parse_monitor_opts Signed-off-by: Mateusz Front --- src/libAtomVM/nifs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 60f6c6005a..295fcf48be 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1325,7 +1325,7 @@ static term parse_monitor_opts(Context *ctx, term monitor_opts, bool *is_alias, } if (UNLIKELY(!term_is_nil(monitor_opts))) { - return RAISE_ERROR(BADARG_ATOM); + RAISE_ERROR(BADARG_ATOM); } return OK_ATOM; From e971ccdce3147b13492a28109834217712753df5 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 4 Feb 2026 11:34:54 +0100 Subject: [PATCH 15/23] Revert ref_data back to ref_ticks in drivers Signed-off-by: Mateusz Front --- .../emscripten/src/lib/websocket_nifs.c | 2 +- .../components/avm_builtins/adc_driver.c | 4 +- .../components/avm_builtins/i2c_resource.c | 54 +++-- .../components/avm_builtins/network_driver.c | 13 +- .../components/avm_builtins/socket_driver.c | 184 +++++++++--------- .../components/avm_builtins/uart_driver.c | 32 ++- .../generic_unix/lib/socket_driver.c | 1 - src/platforms/rp2/src/lib/networkdriver.c | 7 +- 8 files changed, 153 insertions(+), 144 deletions(-) diff --git a/src/platforms/emscripten/src/lib/websocket_nifs.c b/src/platforms/emscripten/src/lib/websocket_nifs.c index ab8e084652..a6d0bc8c89 100644 --- a/src/platforms/emscripten/src/lib/websocket_nifs.c +++ b/src/platforms/emscripten/src/lib/websocket_nifs.c @@ -95,7 +95,7 @@ static void websocket_down(ErlNifEnv *caller_env, void *obj, ErlNifPid *pid, Erl } } -#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(3)) +#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + REF_SIZE + TUPLE_SIZE(3)) static term term_make_websocket_resource(struct WebsocketResource *rsrc, Heap *heap) { diff --git a/src/platforms/esp32/components/avm_builtins/adc_driver.c b/src/platforms/esp32/components/avm_builtins/adc_driver.c index 3a7392e401..67eaa6ac3b 100644 --- a/src/platforms/esp32/components/avm_builtins/adc_driver.c +++ b/src/platforms/esp32/components/avm_builtins/adc_driver.c @@ -352,7 +352,7 @@ static term nif_adc_init(Context *ctx, int argc, term argv[]) enif_release_resource(unit_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', Unit :: resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &unit_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); @@ -504,7 +504,7 @@ static term nif_adc_acquire(Context *ctx, int argc, term argv[]) enif_release_resource(chan_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &chan_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); diff --git a/src/platforms/esp32/components/avm_builtins/i2c_resource.c b/src/platforms/esp32/components/avm_builtins/i2c_resource.c index d4d9caf8db..246b8aa386 100644 --- a/src/platforms/esp32/components/avm_builtins/i2c_resource.c +++ b/src/platforms/esp32/components/avm_builtins/i2c_resource.c @@ -45,14 +45,14 @@ #define TAG "i2c_resource" -#define CHECK_ERROR(ctx, err, msg) \ - if (UNLIKELY(err != ESP_OK)) { \ - ESP_LOGE(TAG, msg ": err: %i.", err); \ - if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { \ - return OUT_OF_MEMORY_ATOM; \ - } \ - return create_error_tuple(ctx, esp_err_to_term(ctx->global, err)); \ - } +#define CHECK_ERROR(ctx, err, msg) \ +if (UNLIKELY(err != ESP_OK)) { \ + ESP_LOGE(TAG, msg ": err: %i.", err); \ + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { \ + return OUT_OF_MEMORY_ATOM; \ + } \ + return create_error_tuple(ctx, esp_err_to_term(ctx->global, err)); \ +} #define ACK_ENABLE true #define MS_TO_TICKS(MS) (MS / portTICK_PERIOD_MS) @@ -230,7 +230,7 @@ static term nif_i2c_open(Context *ctx, int argc, term argv[]) // // {'$i2c', Resource :: resource(), Ref :: reference()} :: i2c() - size_t requested_size = TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; + size_t requested_size = TUPLE_SIZE(3) + REF_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { i2c_driver_delete(i2c_num); ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); @@ -308,7 +308,8 @@ static term nif_i2c_write_bytes(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid); + rsrc_obj->transmitting_pid + ); return create_error_tuple(ctx, reason); } @@ -413,7 +414,8 @@ static term nif_i2c_read_bytes(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid); + rsrc_obj->transmitting_pid + ); return create_error_tuple(ctx, reason); } @@ -519,7 +521,8 @@ static term nif_i2c_begin_transmission(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid); + rsrc_obj->transmitting_pid + ); return create_error_tuple(ctx, reason); } @@ -584,7 +587,8 @@ static term nif_i2c_enqueue_write_bytes(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid); + rsrc_obj->transmitting_pid + ); return create_error_tuple(ctx, reason); } @@ -636,7 +640,8 @@ static term nif_i2c_end_transmission(Context *ctx, int argc, term argv[]) term reason = create_pair( ctx, globalcontext_make_atom(global, EINPROGRESS_ATOMSTR), - rsrc_obj->transmitting_pid); + rsrc_obj->transmitting_pid + ); return create_error_tuple(ctx, reason); } @@ -677,31 +682,38 @@ static const ErlNifResourceTypeInit I2CResourceTypeInit = { .dtor = i2c_resource_dtor, }; -static const struct Nif i2c_open_nif = { +static const struct Nif i2c_open_nif = +{ .base.type = NIFFunctionType, .nif_ptr = nif_i2c_open }; -static const struct Nif i2c_close_nif = { +static const struct Nif i2c_close_nif = +{ .base.type = NIFFunctionType, .nif_ptr = nif_i2c_close }; -static const struct Nif i2c_read_bytes_nif = { +static const struct Nif i2c_read_bytes_nif = +{ .base.type = NIFFunctionType, .nif_ptr = nif_i2c_read_bytes }; -static const struct Nif i2c_write_bytes_nif = { +static const struct Nif i2c_write_bytes_nif = +{ .base.type = NIFFunctionType, .nif_ptr = nif_i2c_write_bytes }; -static const struct Nif i2c_begin_transmission_nif = { +static const struct Nif i2c_begin_transmission_nif = +{ .base.type = NIFFunctionType, .nif_ptr = nif_i2c_begin_transmission }; -static const struct Nif i2c_enqueue_write_bytes_nif = { +static const struct Nif i2c_enqueue_write_bytes_nif = +{ .base.type = NIFFunctionType, .nif_ptr = nif_i2c_enqueue_write_bytes }; -static const struct Nif i2c_end_transmission_nif = { +static const struct Nif i2c_end_transmission_nif = +{ .base.type = NIFFunctionType, .nif_ptr = nif_i2c_end_transmission }; diff --git a/src/platforms/esp32/components/avm_builtins/network_driver.c b/src/platforms/esp32/components/avm_builtins/network_driver.c index 484d39c273..2f07e207ed 100644 --- a/src/platforms/esp32/components/avm_builtins/network_driver.c +++ b/src/platforms/esp32/components/avm_builtins/network_driver.c @@ -58,7 +58,7 @@ #define TCPIP_HOSTNAME_MAX_SIZE 255 #define TAG "network_driver" -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) static const char *const ap_atom = ATOM_STR("\x2", "ap"); static const char *const ap_channel_atom = ATOM_STR("\xA", "ap_channel"); @@ -116,7 +116,10 @@ struct ClientData uint32_t port_process_id; uint32_t owner_process_id; uint64_t ref_ticks; +<<<<<<< HEAD bool managed; +======= +>>>>>>> f036397d (Revert ref_data back to ref_ticks in drivers) }; static inline term make_atom(GlobalContext *global, AtomString atom_str) @@ -137,7 +140,7 @@ static term tuple_from_addr(Heap *heap, uint32_t addr) static void send_term(Heap *heap, struct ClientData *data, term t) { - term ref = term_from_ref_data(data->ref_data, heap); + term ref = term_from_ref_ticks(data->ref_ticks, heap); term msg = term_alloc_tuple(2, heap); term_put_tuple_element(msg, 0, ref); term_put_tuple_element(msg, 1, t); @@ -1044,7 +1047,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) return NativeContinue; } - // TODO: port this code to standard port (and gen_message) + //TODO: port this code to standard port (and gen_message) term pid = term_get_tuple_element(msg, 0); term ref = term_get_tuple_element(msg, 1); term cmd = term_get_tuple_element(msg, 2); @@ -1086,7 +1089,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { ESP_LOGE(TAG, "Unrecognized command: %x", cmd); // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; @@ -1096,7 +1099,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; diff --git a/src/platforms/esp32/components/avm_builtins/socket_driver.c b/src/platforms/esp32/components/avm_builtins/socket_driver.c index 10b141ed75..00fc7d9e3c 100644 --- a/src/platforms/esp32/components/avm_builtins/socket_driver.c +++ b/src/platforms/esp32/components/avm_builtins/socket_driver.c @@ -49,7 +49,7 @@ #include #pragma GCC diagnostic pop -// #define ENABLE_TRACE 1 +//#define ENABLE_TRACE 1 #include "trace.h" #define TAG "socket_driver" @@ -69,8 +69,7 @@ static Context *socket_driver_create_port(GlobalContext *global, term opts); static NativeHandlerResult socket_consume_mailbox(Context *ctx); -static const char *const tcp_error_atom = "\x9" - "tcp_error"; +static const char *const tcp_error_atom = "\x9" "tcp_error"; static const char *const netconn_event_internal = ATOM_STR("\x1E", "$atomvm_netconn_event_internal"); static const char *gen_tcp_moniker_atom = ATOM_STR("\xC", "$avm_gen_tcp"); @@ -78,11 +77,6 @@ static const char *native_tcp_module_atom = ATOM_STR("\xC", "gen_tcp_inet"); static const char *gen_udp_moniker_atom = ATOM_STR("\xC", "$avm_gen_udp"); static const char *native_udp_module_atom = ATOM_STR("\xC", "gen_udp_inet"); -static const RefData no_ref_data = { - .type = RefTypeShort, - .ref_ticks = 0, -}; - static inline term create_socket_wrapper(term pid, const char *moniker_atom, const char *module_atom, Heap *heap, GlobalContext *global) { term tuple = term_alloc_tuple(3, heap); @@ -152,7 +146,7 @@ static term socket_addr_to_tuple(Heap *heap, ip_addr_t *addr) break; } case IPADDR_TYPE_V6: - // TODO: implement IPv6 + //TODO: implement IPv6 addr_tuple = term_invalid_term(); break; @@ -184,7 +178,7 @@ struct SocketData int32_t controlling_process_pid; int32_t passive_receiver_process_pid; - RefData passive_ref_data; + uint64_t passive_ref_ticks; int avail_bytes; @@ -213,7 +207,7 @@ struct TCPServerAccepter { struct ListHead accepter_head; int32_t accepting_process_pid; - RefData ref_data; + uint64_t ref_ticks; }; struct UDPSocketData @@ -230,6 +224,7 @@ struct NetconnEvent u16_t len; }; + struct ReadyConnection { struct ListHead ready_connection_head; @@ -311,7 +306,7 @@ EventListener *socket_events_handler(GlobalContext *glb, EventListener *listener } else { // Add it to ready_connections TRACE("Got event for unknown conn: %p, len = %d adding to ready connections list\n", (void *) event.netconn, event.len); - struct ReadyConnection *ready = (struct ReadyConnection *) malloc(sizeof(struct ReadyConnection)); + struct ReadyConnection *ready = (struct ReadyConnection *) malloc(sizeof (struct ReadyConnection)); ready->netconn = event.netconn; ready->len = event.len; list_append(&platform->ready_connections, &ready->ready_connection_head); @@ -397,7 +392,7 @@ static void socket_data_init(struct SocketData *data, Context *ctx, struct netco list_append(sockets, &data->sockets_head); data->passive_receiver_process_pid = 0; - data->passive_ref_data = no_ref_data; + data->passive_ref_ticks = 0; ctx->platform_data = data; } @@ -443,12 +438,12 @@ static struct UDPSocketData *udp_socket_data_new(Context *ctx, struct netconn *c } // When this method is called, ensure free was called with REPLY_SIZE -#define REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE) -static void do_send_reply(Context *ctx, term reply, RefData ref_data, int32_t pid) +#define REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) +static void do_send_reply(Context *ctx, term reply, uint64_t ref_ticks, int32_t pid) { GlobalContext *glb = ctx->global; term reply_tuple = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(reply_tuple, 0, term_from_ref_data(ref_data, &ctx->heap)); + term_put_tuple_element(reply_tuple, 0, term_from_ref_ticks(ref_ticks, &ctx->heap)); term_put_tuple_element(reply_tuple, 1, reply); globalcontext_send_message(glb, pid, reply_tuple); } @@ -483,7 +478,7 @@ static term lwip_error_atom(GlobalContext *glb, err_t status) } } -static void do_send_error_reply(Context *ctx, err_t status, RefData ref_data, int32_t pid) +static void do_send_error_reply(Context *ctx, err_t status, uint64_t ref_ticks, int32_t pid) { GlobalContext *glb = ctx->global; if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) + REPLY_SIZE) != MEMORY_GC_OK)) { @@ -493,10 +488,10 @@ static void do_send_error_reply(Context *ctx, err_t status, RefData ref_data, in term error_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(error_tuple, 0, ERROR_ATOM); term_put_tuple_element(error_tuple, 1, reason_atom); - do_send_reply(ctx, error_tuple, ref_data, pid); + do_send_reply(ctx, error_tuple, ref_ticks, pid); } -static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, RefData ref_data, int32_t pid) +static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, uint64_t ref_ticks, int32_t pid) { TRACE("Going to accept a TCP connection\n"); GlobalContext *glb = ctx->global; @@ -511,7 +506,7 @@ static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, RefD err_t status = netconn_accept(tcp_data->socket_data.conn, &accepted_conn); if (UNLIKELY(status != ERR_OK)) { socket_data_postinit(platform); - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } @@ -562,7 +557,8 @@ static void accept_conn(Context *ctx, struct TCPServerSocketData *tcp_data, RefD globalcontext_send_message(glb, new_ctx->process_id, message); } - do_send_reply(ctx, result_tuple, ref_data, pid); + do_send_reply(ctx, result_tuple, ref_ticks, pid); + } static void do_accept(Context *ctx, const GenMessage *gen_message) @@ -570,26 +566,26 @@ static void do_accept(Context *ctx, const GenMessage *gen_message) struct TCPServerSocketData *tcp_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (tcp_data->ready_connections) { TRACE("accepting existing connections.\n"); - accept_conn(ctx, tcp_data, ref_data, pid); + accept_conn(ctx, tcp_data, ref_ticks, pid); tcp_data->ready_connections--; } else { struct TCPServerAccepter *accepter = malloc(sizeof(struct TCPServerAccepter)); accepter->accepting_process_pid = pid; - accepter->ref_data = ref_data; + accepter->ref_ticks = ref_ticks; list_append(&tcp_data->accepters_list_head, &accepter->accepter_head); } } static void do_send_passive_reply(Context *ctx, struct SocketData *socket_data, term reply) { - do_send_reply(ctx, reply, socket_data->passive_ref_data, socket_data->passive_receiver_process_pid); + do_send_reply(ctx, reply, socket_data->passive_ref_ticks, socket_data->passive_receiver_process_pid); socket_data->passive_receiver_process_pid = 0; - socket_data->passive_ref_data = no_ref_data; + socket_data->passive_ref_ticks = 0; } static void do_send_socket_error(Context *ctx, err_t status) @@ -613,9 +609,9 @@ static void do_send_socket_error(Context *ctx, err_t status) globalcontext_send_message(glb, socket_data->controlling_process_pid, result_tuple); } } else { - do_send_error_reply(ctx, status, socket_data->passive_ref_data, socket_data->passive_receiver_process_pid); + do_send_error_reply(ctx, status, socket_data->passive_ref_ticks, socket_data->passive_receiver_process_pid); socket_data->passive_receiver_process_pid = 0; - socket_data->passive_ref_data = no_ref_data; + socket_data->passive_ref_ticks = 0; } } @@ -659,7 +655,7 @@ static void do_tcp_server_netconn_event(Context *ctx) } if (accepter) { - accept_conn(ctx, tcp_data, accepter->ref_data, accepter->accepting_process_pid); + accept_conn(ctx, tcp_data, accepter->ref_ticks, accepter->accepting_process_pid); free(accepter); } else { tcp_data->ready_connections++; @@ -767,11 +763,15 @@ static NativeHandlerResult do_receive_data(Context *ctx) netbuf_delete(buf); + if (socket_data->active) { term active_tuple = term_alloc_tuple(socket_data->type == TCPClientSocket ? 3 : 5, &ctx->heap); term_put_tuple_element(active_tuple, 0, socket_data->type == TCPClientSocket ? TCP_ATOM : UDP_ATOM); term socket_pid = term_port_from_local_process_id(ctx->process_id); - term socket_wrapper = socket_data->type == UDPSocket ? create_udp_socket_wrapper(socket_pid, &ctx->heap, ctx->global) : create_tcp_socket_wrapper(socket_pid, &ctx->heap, ctx->global); + term socket_wrapper = + socket_data->type == UDPSocket ? + create_udp_socket_wrapper(socket_pid, &ctx->heap, ctx->global) : + create_tcp_socket_wrapper(socket_pid, &ctx->heap, ctx->global); term_put_tuple_element(active_tuple, 1, socket_wrapper); if (socket_data->type == TCPClientSocket) { term_put_tuple_element(active_tuple, 2, recv_term); @@ -782,9 +782,9 @@ static NativeHandlerResult do_receive_data(Context *ctx) } globalcontext_send_message(ctx->global, socket_data->controlling_process_pid, active_tuple); TRACE("sent received to active process (pid=%d): ", (int) socket_data->controlling_process_pid); -#ifdef ENABLE_TRACE - term_display(stdout, active_tuple, ctx); -#endif + #ifdef ENABLE_TRACE + term_display(stdout, active_tuple, ctx); + #endif TRACE("\n"); } else { term ok_tuple = term_alloc_tuple(2, &ctx->heap); @@ -792,9 +792,9 @@ static NativeHandlerResult do_receive_data(Context *ctx) term_put_tuple_element(ok_tuple, 1, recv_term); do_send_passive_reply(ctx, socket_data, ok_tuple); TRACE("sent received to passive caller (pid=%d): ", (int) socket_data->passive_receiver_process_pid); -#ifdef ENABLE_TRACE - term_display(stdout, ok_tuple, ctx); -#endif + #ifdef ENABLE_TRACE + term_display(stdout, ok_tuple, ctx); + #endif TRACE("\n"); } @@ -862,7 +862,7 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) struct ESP32PlatformData *platform = glb->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); return; @@ -877,25 +877,25 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) bool ok = term_is_local_pid(controlling_process_term); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } int32_t controlling_process_pid = term_to_local_process_id(controlling_process_term); int ok_int; char *address_string = interop_term_to_string(address_term, &ok_int); if (UNLIKELY(!ok_int)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } avm_int_t port = term_to_int(port_term); bool active = bool_term_to_bool(active_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } bool binary = bool_term_to_bool(binary_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } @@ -906,12 +906,12 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) TRACE("tcp: connecting to: %s\n", address_string); ip_addr_t remote_ip; - // TODO: use dns_gethostbyname instead + //TODO: use dns_gethostbyname instead err_t status = netconn_gethostbyname(address_string, &remote_ip); if (UNLIKELY(status != ERR_OK)) { free(address_string); TRACE("tcp: host resolution failed.\n"); - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } @@ -929,7 +929,7 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) status = netconn_connect(conn, &remote_ip, port); if (UNLIKELY(status != ERR_OK)) { TRACE("tcp: failed connect: %i\n", status); - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } @@ -943,7 +943,7 @@ static void do_connect(Context *ctx, const GenMessage *gen_message) tcp_data->socket_data.active = active; tcp_data->socket_data.binary = binary; - do_send_reply(ctx, OK_ATOM, ref_data, pid); + do_send_reply(ctx, OK_ATOM, ref_ticks, pid); } static void do_listen(Context *ctx, const GenMessage *gen_message) @@ -952,7 +952,7 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) struct ESP32PlatformData *platform = glb->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); return; @@ -970,12 +970,12 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) bool ok; bool active = bool_term_to_bool(active_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } bool binary = bool_term_to_bool(binary_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } @@ -987,7 +987,7 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) err_t status = netconn_bind(conn, IP_ADDR_ANY, port); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } @@ -995,13 +995,13 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) u16_t nport; status = netconn_getaddr(conn, &naddr, &nport, 1); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } status = netconn_listen_with_backlog(conn, backlog); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } @@ -1018,7 +1018,7 @@ static void do_listen(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_data, pid); + do_send_reply(ctx, OK_ATOM, ref_ticks, pid); } void do_udp_open(Context *ctx, const GenMessage *gen_message) @@ -1027,7 +1027,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) struct ESP32PlatformData *platform = glb->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); return; @@ -1041,19 +1041,19 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) bool ok = term_is_local_pid(controlling_process_term); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } int32_t controlling_process_pid = term_to_local_process_id(controlling_process_term); avm_int_t port = term_to_int(port_term); bool active = bool_term_to_bool(active_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } bool binary = bool_term_to_bool(binary_term, &ok); if (UNLIKELY(!ok)) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } @@ -1076,7 +1076,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) if (port != 0) { err_t status = netconn_bind(conn, IP_ADDR_ANY, port); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } } @@ -1085,7 +1085,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) u16_t nport; err_t status = netconn_getaddr(conn, &naddr, &nport, 1); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } udp_data->socket_data.port = nport; @@ -1093,7 +1093,7 @@ void do_udp_open(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_data, pid); + do_send_reply(ctx, OK_ATOM, ref_ticks, pid); } // Required for compatibility with existing erlang libraries @@ -1125,7 +1125,7 @@ static void do_send(Context *ctx, const GenMessage *gen_message) struct TCPServerSocketData *tcp_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); @@ -1167,7 +1167,7 @@ static void do_send(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_data, pid); + do_send_reply(ctx, OK_ATOM, ref_ticks, pid); } static void do_sendto(Context *ctx, const GenMessage *gen_message) @@ -1175,7 +1175,7 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) struct UDPSocketData *udp_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 4)) { ESP_LOGW(TAG, "Received invalid message."); @@ -1191,10 +1191,10 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) case InteropOk: break; case InteropMemoryAllocFail: - do_send_error_reply(ctx, ERR_MEM, ref_data, pid); + do_send_error_reply(ctx, ERR_MEM, ref_ticks, pid); return; case InteropBadArg: - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } void *buffer = malloc(buffer_size); @@ -1203,11 +1203,11 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) break; case InteropMemoryAllocFail: free(buffer); - do_send_error_reply(ctx, ERR_MEM, ref_data, pid); + do_send_error_reply(ctx, ERR_MEM, ref_ticks, pid); return; case InteropBadArg: free(buffer); - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); return; } @@ -1221,7 +1221,7 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) if (UNLIKELY(status != ERR_OK)) { netbuf_delete(sendbuf); free(buffer); - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } @@ -1230,14 +1230,14 @@ static void do_sendto(Context *ctx, const GenMessage *gen_message) free(buffer); if (UNLIKELY(status != ERR_OK)) { - do_send_error_reply(ctx, status, ref_data, pid); + do_send_error_reply(ctx, status, ref_ticks, pid); return; } if (UNLIKELY(memory_ensure_free(ctx, REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); } - do_send_reply(ctx, OK_ATOM, ref_data, pid); + do_send_reply(ctx, OK_ATOM, ref_ticks, pid); } static void do_close(Context *ctx, const GenMessage *gen_message) @@ -1245,7 +1245,7 @@ static void do_close(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) + REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); @@ -1263,11 +1263,11 @@ static void do_close(Context *ctx, const GenMessage *gen_message) synclist_remove(&platform->sockets, &socket_data->sockets_head); if (UNLIKELY(close_disconnect_res != ERR_OK)) { - do_send_error_reply(ctx, close_disconnect_res, ref_data, pid); + do_send_error_reply(ctx, close_disconnect_res, ref_ticks, pid); } else if (UNLIKELY(delete_res != ERR_OK)) { - do_send_error_reply(ctx, delete_res, ref_data, pid); + do_send_error_reply(ctx, delete_res, ref_ticks, pid); } else { - do_send_reply(ctx, OK_ATOM, ref_data, pid); + do_send_reply(ctx, OK_ATOM, ref_ticks, pid); } } @@ -1276,16 +1276,16 @@ static NativeHandlerResult do_recvfrom(Context *ctx, const GenMessage *gen_messa struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); // We cannot stack blocked queries if (socket_data->passive_receiver_process_pid != 0) { - do_send_error_reply(ctx, ERR_ALREADY, ref_data, pid); + do_send_error_reply(ctx, ERR_ALREADY, ref_ticks, pid); return NativeContinue; } socket_data->passive_receiver_process_pid = pid; - socket_data->passive_ref_data = ref_data; + socket_data->passive_ref_ticks = ref_ticks; // There may be nothing to read. if (socket_data->avail_bytes <= 0 && !socket_data->read_condition) { @@ -1300,10 +1300,10 @@ static void do_get_port(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (socket_data->port == 0) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); } else { if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) + REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); @@ -1311,7 +1311,7 @@ static void do_get_port(Context *ctx, const GenMessage *gen_message) term ok_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(ok_tuple, 0, OK_ATOM); term_put_tuple_element(ok_tuple, 1, term_from_int(socket_data->port)); - do_send_reply(ctx, ok_tuple, ref_data, pid); + do_send_reply(ctx, ok_tuple, ref_ticks, pid); } } @@ -1320,14 +1320,14 @@ static void do_sockname(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); ip_addr_t addr; u16_t port; err_t result = netconn_addr(socket_data->conn, &addr, &port); term return_msg; if (result != ERR_OK) { - do_send_error_reply(ctx, result, ref_data, pid); + do_send_error_reply(ctx, result, ref_ticks, pid); } else { if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + SOCKET_INET_ADDR + REPLY_SIZE) != MEMORY_GC_OK)) { AVM_ABORT(); @@ -1340,7 +1340,7 @@ static void do_sockname(Context *ctx, const GenMessage *gen_message) term_put_tuple_element(address_port_term, 1, port_term); term_put_tuple_element(return_msg, 0, OK_ATOM); term_put_tuple_element(return_msg, 1, address_port_term); - do_send_reply(ctx, return_msg, ref_data, pid); + do_send_reply(ctx, return_msg, ref_ticks, pid); } } @@ -1349,14 +1349,14 @@ static void do_peername(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); ip_addr_t addr; u16_t port; err_t result = netconn_peer(socket_data->conn, &addr, &port); term return_msg; if (result != ERR_OK) { - do_send_error_reply(ctx, result, ref_data, pid); + do_send_error_reply(ctx, result, ref_ticks, pid); } else { // {ok, {{A,B,C,D}, Port}} if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + SOCKET_INET_ADDR + REPLY_SIZE) != MEMORY_GC_OK)) { @@ -1370,7 +1370,7 @@ static void do_peername(Context *ctx, const GenMessage *gen_message) term_put_tuple_element(address_port_term, 1, port_term); term_put_tuple_element(return_msg, 0, OK_ATOM); term_put_tuple_element(return_msg, 1, address_port_term); - do_send_reply(ctx, return_msg, ref_data, pid); + do_send_reply(ctx, return_msg, ref_ticks, pid); } } @@ -1379,7 +1379,7 @@ static void do_controlling_process(Context *ctx, const GenMessage *gen_message) struct SocketData *socket_data = ctx->platform_data; int32_t pid = term_to_local_process_id(gen_message->pid); - RefData ref_data = term_to_ref_data(gen_message->ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message->ref); if (UNLIKELY(term_get_tuple_arity(gen_message->req) != 2)) { ESP_LOGW(TAG, "Received invalid message."); @@ -1387,7 +1387,7 @@ static void do_controlling_process(Context *ctx, const GenMessage *gen_message) } term new_pid_term = term_get_tuple_element(gen_message->req, 1); if (UNLIKELY(!term_is_local_pid(new_pid_term))) { - do_send_error_reply(ctx, ERR_ARG, ref_data, pid); + do_send_error_reply(ctx, ERR_ARG, ref_ticks, pid); } else { term return_msg; @@ -1404,7 +1404,7 @@ static void do_controlling_process(Context *ctx, const GenMessage *gen_message) socket_data->controlling_process_pid = new_pid; return_msg = OK_ATOM; } - do_send_reply(ctx, return_msg, ref_data, pid); + do_send_reply(ctx, return_msg, ref_ticks, pid); } } @@ -1418,9 +1418,9 @@ static NativeHandlerResult socket_consume_mailbox(Context *ctx) term msg = message->message; TRACE("message: "); -#ifdef ENABLE_TRACE - term_display(stdout, msg, ctx); -#endif + #ifdef ENABLE_TRACE + term_display(stdout, msg, ctx); + #endif TRACE("\n"); if (term_is_tuple(msg) && term_get_tuple_element(msg, 0) == globalcontext_make_atom(glb, netconn_event_internal)) { @@ -1445,7 +1445,7 @@ static NativeHandlerResult socket_consume_mailbox(Context *ctx) term cmd_name = term_get_tuple_element(gen_message.req, 0); switch (cmd_name) { - // TODO: remove this + //TODO: remove this case INIT_ATOM: TRACE("init\n"); do_init(ctx, &gen_message); diff --git a/src/platforms/esp32/components/avm_builtins/uart_driver.c b/src/platforms/esp32/components/avm_builtins/uart_driver.c index 3d991d0bee..3f73369876 100644 --- a/src/platforms/esp32/components/avm_builtins/uart_driver.c +++ b/src/platforms/esp32/components/avm_builtins/uart_driver.c @@ -56,6 +56,7 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx); #define TAG "uart_driver" #define UART_BUF_SIZE 256 +#define NO_REF 0 #define NO_READER term_invalid_term() #define PIN_ERROR -2 @@ -63,17 +64,12 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx); #define GPIO_NUM_MAX SOC_GPIO_PIN_COUNT #endif -static const RefData no_ref_data = { - .type = RefTypeShort, - .ref_ticks = 0, -}; - struct UARTData { QueueHandle_t rxqueue; EventListener listener; term reader_process_pid; - RefData reader_ref_data; + uint64_t reader_ref_ticks; uint8_t uart_num; #ifndef AVM_NO_SMP Mutex *reader_lock; @@ -111,11 +107,11 @@ static const AtomStringIntPair cmd_table[] = { SELECT_INT_DEFAULT(UARTInvalidCmd) }; -static void safe_update_reader_data(struct UARTData *uart_data, term pid, RefData ref_data) +static void safe_update_reader_data(struct UARTData *uart_data, term pid, uint64_t ref_ticks) { SMP_MUTEX_LOCK(uart_data->reader_lock); uart_data->reader_process_pid = pid; - uart_data->reader_ref_data = ref_data; + uart_data->reader_ref_ticks = ref_ticks; SMP_MUTEX_UNLOCK(uart_data->reader_lock); } @@ -132,7 +128,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen int bin_size = term_binary_heap_size(event.size); Heap heap; - if (UNLIKELY(memory_init_heap(&heap, bin_size + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_init_heap(&heap, bin_size + REF_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); AVM_ABORT(); } @@ -145,7 +141,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen term_put_tuple_element(ok_tuple, 0, OK_ATOM); term_put_tuple_element(ok_tuple, 1, bin); - term ref = term_from_ref_data(uart_data->reader_ref_data, &heap); + term ref = term_from_ref_ticks(uart_data->reader_ref_ticks, &heap); term result_tuple = term_alloc_tuple(2, &heap); term_put_tuple_element(result_tuple, 0, ref); @@ -155,7 +151,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen globalcontext_send_message(glb, local_pid, result_tuple); memory_destroy_heap(&heap, glb); - safe_update_reader_data(uart_data, NO_READER, no_ref_data); + safe_update_reader_data(uart_data, NO_READER, NO_REF); } break; case UART_FIFO_OVF: @@ -327,7 +323,7 @@ Context *uart_driver_create_port(GlobalContext *global, term opts) uart_data->listener.handler = uart_interrupt_callback; sys_register_listener(global, &uart_data->listener); uart_data->reader_process_pid = term_invalid_term(); - uart_data->reader_ref_data = no_ref_data; + uart_data->reader_ref_ticks = 0; uart_data->uart_num = uart_num; ctx->native_handler = uart_driver_consume_mailbox; ctx->platform_data = uart_data; @@ -357,7 +353,7 @@ static void uart_driver_do_read(Context *ctx, GenMessage gen_message) struct UARTData *uart_data = ctx->platform_data; term pid = gen_message.pid; term ref = gen_message.ref; - RefData ref_data = term_to_ref_data(ref); + uint64_t ref_ticks = term_to_ref_ticks(ref); int local_pid = term_to_local_process_id(pid); @@ -395,7 +391,7 @@ static void uart_driver_do_read(Context *ctx, GenMessage gen_message) port_send_reply(ctx, pid, ref, ok_tuple); } else { - safe_update_reader_data(uart_data, pid, ref_data); + safe_update_reader_data(uart_data, pid, ref_ticks); } } @@ -403,7 +399,7 @@ static void uart_driver_do_cancel_read(Context *ctx, GenMessage gen_message) { struct UARTData *uart_data = ctx->platform_data; - safe_update_reader_data(uart_data, NO_READER, no_ref_data); + safe_update_reader_data(uart_data, NO_READER, NO_REF); term pid = gen_message.pid; term ref = gen_message.ref; @@ -510,12 +506,12 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx) return NativeContinue; } - RefData ref_data = term_to_ref_data(gen_message.ref); + uint64_t ref_ticks = term_to_ref_ticks(gen_message.ref); int local_pid = term_to_local_process_id(gen_message.pid); if (is_closed) { - if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + TERM_BOXED_REFERENCE_PROCESS_SIZE) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + REF_SIZE) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "[uart_driver_consume_mailbox] Failed to allocate space for error tuple"); globalcontext_send_message(glb, local_pid, OUT_OF_MEMORY_ATOM); } @@ -525,7 +521,7 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx) term_put_tuple_element(error_tuple, 1, NOPROC_ATOM); term result_tuple = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(result_tuple, 0, term_from_ref_data(ref_data, &ctx->heap)); + term_put_tuple_element(result_tuple, 0, term_from_ref_ticks(ref_ticks, &ctx->heap)); term_put_tuple_element(result_tuple, 1, error_tuple); globalcontext_send_message(glb, local_pid, result_tuple); diff --git a/src/platforms/generic_unix/lib/socket_driver.c b/src/platforms/generic_unix/lib/socket_driver.c index a46b99d162..7e9439622d 100644 --- a/src/platforms/generic_unix/lib/socket_driver.c +++ b/src/platforms/generic_unix/lib/socket_driver.c @@ -67,7 +67,6 @@ typedef struct PassiveRecvListener size_t length; size_t buffer; term controlling_process; - // FIXME change to ref data uint64_t ref_ticks; } PassiveRecvListener; diff --git a/src/platforms/rp2/src/lib/networkdriver.c b/src/platforms/rp2/src/lib/networkdriver.c index 0f0285cb99..bdef70eb75 100644 --- a/src/platforms/rp2/src/lib/networkdriver.c +++ b/src/platforms/rp2/src/lib/networkdriver.c @@ -41,7 +41,7 @@ #pragma GCC diagnostic pop -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) #define DEFAULT_HOSTNAME_FMT "atomvm-%02x%02x%02x%02x%02x%02x" #define DEFAULT_HOSTNAME_SIZE (strlen("atomvm-") + 12 + 1) @@ -79,7 +79,6 @@ struct NetworkDriverData { GlobalContext *global; uint32_t owner_process_id; - // FIXME change to ref data uint64_t ref_ticks; int link_status; char *sntp_hostname; @@ -759,7 +758,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } @@ -768,7 +767,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_PROCESS_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } From 99d10402dc5b7bb86be0c15dd698dd65e728bc63 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 4 Feb 2026 11:37:41 +0100 Subject: [PATCH 16/23] REF_SIZE -> TERM_BOXED_SHORT_REFERENCE_SIZE in drivers Signed-off-by: Mateusz Front --- src/platforms/emscripten/src/lib/websocket_nifs.c | 2 +- src/platforms/esp32/components/avm_builtins/adc_driver.c | 4 ++-- src/platforms/esp32/components/avm_builtins/i2c_resource.c | 2 +- .../esp32/components/avm_builtins/network_driver.c | 6 +++--- src/platforms/esp32/components/avm_builtins/socket_driver.c | 2 +- src/platforms/esp32/components/avm_builtins/uart_driver.c | 4 ++-- src/platforms/rp2/src/lib/networkdriver.c | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/platforms/emscripten/src/lib/websocket_nifs.c b/src/platforms/emscripten/src/lib/websocket_nifs.c index a6d0bc8c89..ab8e084652 100644 --- a/src/platforms/emscripten/src/lib/websocket_nifs.c +++ b/src/platforms/emscripten/src/lib/websocket_nifs.c @@ -95,7 +95,7 @@ static void websocket_down(ErlNifEnv *caller_env, void *obj, ErlNifPid *pid, Erl } } -#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + REF_SIZE + TUPLE_SIZE(3)) +#define TERM_WEBSOCKET_RESOURCE_SIZE (TERM_BOXED_RESOURCE_SIZE + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(3)) static term term_make_websocket_resource(struct WebsocketResource *rsrc, Heap *heap) { diff --git a/src/platforms/esp32/components/avm_builtins/adc_driver.c b/src/platforms/esp32/components/avm_builtins/adc_driver.c index 67eaa6ac3b..3a7392e401 100644 --- a/src/platforms/esp32/components/avm_builtins/adc_driver.c +++ b/src/platforms/esp32/components/avm_builtins/adc_driver.c @@ -352,7 +352,7 @@ static term nif_adc_init(Context *ctx, int argc, term argv[]) enif_release_resource(unit_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', Unit :: resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &unit_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); @@ -504,7 +504,7 @@ static term nif_adc_acquire(Context *ctx, int argc, term argv[]) enif_release_resource(chan_rsrc); // decrement refcount after enif_alloc_resource // {ok, {'$adc', resource(), ref()}} - size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; ESP_LOGD(TAG, "Requesting memory size %u for return message", requested_size); if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &chan_obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "failed to allocate tuple memory size %u: %s:%i.", requested_size, __FILE__, __LINE__); diff --git a/src/platforms/esp32/components/avm_builtins/i2c_resource.c b/src/platforms/esp32/components/avm_builtins/i2c_resource.c index 246b8aa386..5df5ecdcf7 100644 --- a/src/platforms/esp32/components/avm_builtins/i2c_resource.c +++ b/src/platforms/esp32/components/avm_builtins/i2c_resource.c @@ -230,7 +230,7 @@ static term nif_i2c_open(Context *ctx, int argc, term argv[]) // // {'$i2c', Resource :: resource(), Ref :: reference()} :: i2c() - size_t requested_size = TUPLE_SIZE(3) + REF_SIZE; + size_t requested_size = TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_SHORT_SIZE; if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { i2c_driver_delete(i2c_num); ESP_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__); diff --git a/src/platforms/esp32/components/avm_builtins/network_driver.c b/src/platforms/esp32/components/avm_builtins/network_driver.c index 2f07e207ed..61369a7c69 100644 --- a/src/platforms/esp32/components/avm_builtins/network_driver.c +++ b/src/platforms/esp32/components/avm_builtins/network_driver.c @@ -58,7 +58,7 @@ #define TCPIP_HOSTNAME_MAX_SIZE 255 #define TAG "network_driver" -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE) static const char *const ap_atom = ATOM_STR("\x2", "ap"); static const char *const ap_channel_atom = ATOM_STR("\xA", "ap_channel"); @@ -1089,7 +1089,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { ESP_LOGE(TAG, "Unrecognized command: %x", cmd); // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; @@ -1099,7 +1099,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "Unable to allocate heap space for error; no message sent"); return NativeContinue; diff --git a/src/platforms/esp32/components/avm_builtins/socket_driver.c b/src/platforms/esp32/components/avm_builtins/socket_driver.c index 00fc7d9e3c..feb75178e2 100644 --- a/src/platforms/esp32/components/avm_builtins/socket_driver.c +++ b/src/platforms/esp32/components/avm_builtins/socket_driver.c @@ -438,7 +438,7 @@ static struct UDPSocketData *udp_socket_data_new(Context *ctx, struct netconn *c } // When this method is called, ensure free was called with REPLY_SIZE -#define REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) +#define REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE) static void do_send_reply(Context *ctx, term reply, uint64_t ref_ticks, int32_t pid) { GlobalContext *glb = ctx->global; diff --git a/src/platforms/esp32/components/avm_builtins/uart_driver.c b/src/platforms/esp32/components/avm_builtins/uart_driver.c index 3f73369876..e913057629 100644 --- a/src/platforms/esp32/components/avm_builtins/uart_driver.c +++ b/src/platforms/esp32/components/avm_builtins/uart_driver.c @@ -128,7 +128,7 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen int bin_size = term_binary_heap_size(event.size); Heap heap; - if (UNLIKELY(memory_init_heap(&heap, bin_size + REF_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_init_heap(&heap, bin_size + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(2) * 2) != MEMORY_GC_OK)) { fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); AVM_ABORT(); } @@ -511,7 +511,7 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx) int local_pid = term_to_local_process_id(gen_message.pid); if (is_closed) { - if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + REF_SIZE) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2) * 2 + TERM_BOXED_REFERENCE_SHORT_SIZE) != MEMORY_GC_OK)) { ESP_LOGE(TAG, "[uart_driver_consume_mailbox] Failed to allocate space for error tuple"); globalcontext_send_message(glb, local_pid, OUT_OF_MEMORY_ATOM); } diff --git a/src/platforms/rp2/src/lib/networkdriver.c b/src/platforms/rp2/src/lib/networkdriver.c index bdef70eb75..374f9e85f0 100644 --- a/src/platforms/rp2/src/lib/networkdriver.c +++ b/src/platforms/rp2/src/lib/networkdriver.c @@ -41,7 +41,7 @@ #pragma GCC diagnostic pop -#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) +#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE) #define DEFAULT_HOSTNAME_FMT "atomvm-%02x%02x%02x%02x%02x%02x" #define DEFAULT_HOSTNAME_SIZE (strlen("atomvm-") + 12 + 1) @@ -758,7 +758,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) default: { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } @@ -767,7 +767,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) } } else { // {Ref, {error, badarg}} - size_t heap_size = TUPLE_SIZE(2) + REF_SIZE + TUPLE_SIZE(2); + size_t heap_size = TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_SHORT_SIZE + TUPLE_SIZE(2); if (UNLIKELY(memory_ensure_free(ctx, heap_size) != MEMORY_GC_OK)) { return NativeContinue; } From 250fb3a10d68e9159254bf268e7555a7465e9aff Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 4 Feb 2026 11:59:55 +0100 Subject: [PATCH 17/23] Add aliases to test_refs_ordering Signed-off-by: Mateusz Front --- tests/erlang_tests/test_refs_ordering.erl | 34 ++++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/tests/erlang_tests/test_refs_ordering.erl b/tests/erlang_tests/test_refs_ordering.erl index 5071ae5633..10616098b8 100644 --- a/tests/erlang_tests/test_refs_ordering.erl +++ b/tests/erlang_tests/test_refs_ordering.erl @@ -20,17 +20,17 @@ -module(test_refs_ordering). --export([start/0, sort/1, insert/2, check/2, get_ref/2]). +-export([start/0, sort/1, insert/2, check/2, get_ref/3, make_alias_ref/0]). start() -> - A = get_ref(3, []), - B = get_ref(7, []), - C = get_ref(1, []), - D = get_ref(3, []), - E = get_ref(4, []), + A = get_ref(3, [], fun make_ref/0), + B = get_ref(7, [], fun make_alias_ref/0), + C = get_ref(1, [], fun make_ref/0), + D = get_ref(3, [], fun make_alias_ref/0), + E = get_ref(4, [], fun make_ref/0), Sorted = sort([E, C, D, A, B]), check(Sorted, [A, B, C, D, E]) + - bool_to_n(Sorted < [make_ref()]) * 2 + + bool_to_n(Sorted < [make_alias_ref()]) * 2 + bool_to_n(Sorted > {make_ref()}) * 4. sort(L) -> @@ -57,12 +57,26 @@ check(T, Expected) when T == Expected -> check(T, Expected) when T /= Expected -> 0. -get_ref(0, Acc) -> +get_ref(0, Acc, _Generator) -> Acc; -get_ref(N, _Acc) -> - get_ref(N - 1, make_ref()). +get_ref(N, _Acc, Generator) -> + get_ref(N - 1, Generator(), Generator). bool_to_n(true) -> 1; bool_to_n(false) -> 0. + +make_alias_ref() -> + AliasesAvailable = + case erlang:system_info(machine) of + "ATOM" -> true; + "BEAM" -> erlang:system_info(otp_release) >= "24" + end, + if + AliasesAvailable, + true -> + erlang:alias(); + false -> + make_ref() + end. From 18289cc29a18df04375d69c905dcd7d98b68cca7 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Wed, 4 Feb 2026 14:50:43 +0100 Subject: [PATCH 18/23] Make comparing process refs compliant with the BEAM Signed-off-by: Mateusz Front --- src/libAtomVM/term.c | 13 +++++++++++++ tests/erlang_tests/test_refs_ordering.erl | 11 ++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/libAtomVM/term.c b/src/libAtomVM/term.c index d8916e588f..ac5110fa56 100644 --- a/src/libAtomVM/term.c +++ b/src/libAtomVM/term.c @@ -682,11 +682,15 @@ TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalC uint32_t len, other_len; if (term_is_resource_reference(t)) { len = 4; + } else if (term_is_process_reference(t)) { + len = 3; } else { len = 2; } if (term_is_resource_reference(other)) { other_len = 4; + } else if (term_is_process_reference(other)) { + other_len = 3; } else { other_len = 2; } @@ -700,6 +704,15 @@ TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalC int64_t other_ticks = term_to_ref_ticks(other); other_data[0] = other_ticks >> 32; other_data[1] = (uint32_t) other_ticks; + } else if (len == 3) { + data[0] = term_process_ref_to_process_id(t); + int64_t t_ticks = term_to_ref_ticks(t); + data[1] = t_ticks >> 32; + data[2] = (uint32_t) t_ticks; + other_data[0] = term_process_ref_to_process_id(other); + int64_t other_ticks = term_to_ref_ticks(other); + other_data[1] = other_ticks >> 32; + other_data[2] = (uint32_t) other_ticks; } else { // len == 4 struct RefcBinary *refc = term_resource_refc_binary_ptr(t); diff --git a/tests/erlang_tests/test_refs_ordering.erl b/tests/erlang_tests/test_refs_ordering.erl index 10616098b8..f9e95c2f44 100644 --- a/tests/erlang_tests/test_refs_ordering.erl +++ b/tests/erlang_tests/test_refs_ordering.erl @@ -29,7 +29,9 @@ start() -> D = get_ref(3, [], fun make_alias_ref/0), E = get_ref(4, [], fun make_ref/0), Sorted = sort([E, C, D, A, B]), - check(Sorted, [A, B, C, D, E]) + + erlang:display([A, C, E, B, D]), + erlang:display(Sorted), + check(Sorted, [A, C, E, B, D]) + bool_to_n(Sorted < [make_alias_ref()]) * 2 + bool_to_n(Sorted > {make_ref()}) * 4. @@ -74,9 +76,8 @@ make_alias_ref() -> "BEAM" -> erlang:system_info(otp_release) >= "24" end, if - AliasesAvailable, - true -> + AliasesAvailable -> erlang:alias(); - false -> - make_ref() + true -> + {mock_alias_ref, make_ref()} end. From 3ad27e34847e3167e13ef4c029149596e67be59c Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Fri, 6 Feb 2026 15:19:44 +0100 Subject: [PATCH 19/23] CR Signed-off-by: Mateusz Front --- src/libAtomVM/context.c | 2 +- src/libAtomVM/nifs.c | 16 +++++---- src/libAtomVM/term.h | 44 +++++------------------ tests/erlang_tests/test_refs_ordering.erl | 2 -- 4 files changed, 19 insertions(+), 45 deletions(-) diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index 4d07ee1bdc..8c79f0ac56 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -787,7 +787,7 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) AVM_ABORT(); } // Prepare the message on ctx's heap which will be freed afterwards. - term ref = term_from_ref_data(monitored_monitor->ref_data, &ctx->heap); + term ref = term_from_ref_data(&monitored_monitor->ref_data, &ctx->heap); term port_or_process = term_pid_or_port_from_context(ctx); term port_or_process_atom diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 295fcf48be..4103cef687 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1221,7 +1221,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) term pid = term_get_tuple_element(msg, 1); term ref = term_get_tuple_element(msg, 2); term req = term_get_tuple_element(msg, 3); - RefData ref_data = term_to_ref_data(ref); + uint64_t ref_ticks = term_to_ref_ticks(ref); if (is_tagged_tuple(req, PUT_CHARS_ATOM, 3)) { term chars = term_get_tuple_element(req, 2); @@ -1231,7 +1231,7 @@ static NativeHandlerResult process_console_message(Context *ctx, term msg) printf("%s", str); free(str); - term refcopy = term_from_ref_data(ref_data, &ctx->heap); + term refcopy = term_from_ref_ticks(ref_ticks, &ctx->heap); term reply = term_alloc_tuple(3, &ctx->heap); term_put_tuple_element(reply, 0, IO_REPLY_ATOM); @@ -1487,7 +1487,7 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free scheduler_init_ready(new_ctx); - term ref = term_from_ref_data(ref_data, &ctx->heap); + term ref = term_from_ref_data(&ref_data, &ctx->heap); term process_ref_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(process_ref_tuple, 0, new_pid); @@ -4528,7 +4528,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - return term_from_ref_data(ref_data, &ctx->heap); + return term_from_ref_data(&ref_data, &ctx->heap); } static term nif_erlang_demonitor(Context *ctx, int argc, term argv[]) @@ -6697,9 +6697,11 @@ static term nif_erlang_alias(Context *ctx, int argc, term argv[]) RefData ref_data = { .type = RefTypeProcess, - .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } + .process = { + .ref_ticks = globalcontext_get_ref_ticks(ctx->global), + .process_id = ctx->process_id } }; - term process_ref = term_from_ref_data(ref_data, &ctx->heap); + term process_ref = term_from_ref_data(&ref_data, &ctx->heap); struct Monitor *monitor = monitor_alias_new(ref_data, ContextMonitorAliasExplicitUnalias); if (IS_NULL_PTR(monitor)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); @@ -6713,9 +6715,9 @@ static term nif_erlang_unalias(Context *ctx, int argc, term argv[]) UNUSED(argc); term process_ref = argv[0]; - VALIDATE_VALUE(process_ref, term_is_local_reference); uint64_t ref_ticks = term_to_ref_ticks(process_ref); + struct MonitorAlias *alias = context_find_alias(ctx, ref_ticks); if (IS_NULL_PTR(alias)) { return FALSE_ATOM; diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 87534e0f92..0411900a44 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -3122,50 +3122,24 @@ static inline term term_from_resource(void *resource, Heap *heap) return ret; } -static inline RefData term_to_ref_data(term t) -{ - TERM_DEBUG_ASSERT(term_is_reference(t)); - - RefData ref_data = { 0 }; // Needed for GCC 10 - if (term_is_external_reference(t)) { - ref_data.type = RefTypeExternal; - ref_data.external.node = term_get_external_node(t); - ref_data.external.creation = term_get_external_node_creation(t); - ref_data.external.len = term_get_external_reference_len(t); - ref_data.external.words = term_get_external_reference_words(t); - } else if (term_is_process_reference(t)) { - ref_data.type = RefTypeProcess; - ref_data.process.ref_ticks = term_to_ref_ticks(t); - ref_data.process.process_id = term_process_ref_to_process_id(t); - } else if (term_is_resource_reference(t)) { - ref_data.type = RefTypeResource; - ref_data.resource = &term_resource_refc_binary_ptr(t)->data; - } else { - ref_data.type = RefTypeShort; - ref_data.ref_ticks = term_to_ref_ticks(t); - } - - return ref_data; -} - -static inline term term_from_ref_data(RefData ref_data, Heap *heap) +static inline term term_from_ref_data(RefData *ref_data, Heap *heap) { - switch (ref_data.type) { + switch (ref_data->type) { case RefTypeShort: { - return term_from_ref_ticks(ref_data.ref_ticks, heap); + return term_from_ref_ticks(ref_data->ref_ticks, heap); } case RefTypeProcess: { - return term_make_process_reference(ref_data.process.process_id, ref_data.ref_ticks, heap); + return term_make_process_reference(ref_data->process.process_id, ref_data->ref_ticks, heap); } case RefTypeResource: { - return term_from_resource(ref_data.resource, heap); + return term_from_resource(ref_data->resource, heap); } case RefTypeExternal: { return term_make_external_reference( - ref_data.external.node, - ref_data.external.len, - ref_data.external.words, - ref_data.external.creation, + ref_data->external.node, + ref_data->external.len, + ref_data->external.words, + ref_data->external.creation, heap); } default: { diff --git a/tests/erlang_tests/test_refs_ordering.erl b/tests/erlang_tests/test_refs_ordering.erl index f9e95c2f44..6b54f8bf24 100644 --- a/tests/erlang_tests/test_refs_ordering.erl +++ b/tests/erlang_tests/test_refs_ordering.erl @@ -29,8 +29,6 @@ start() -> D = get_ref(3, [], fun make_alias_ref/0), E = get_ref(4, [], fun make_ref/0), Sorted = sort([E, C, D, A, B]), - erlang:display([A, C, E, B, D]), - erlang:display(Sorted), check(Sorted, [A, C, E, B, D]) + bool_to_n(Sorted < [make_alias_ref()]) * 2 + bool_to_n(Sorted > {make_ref()}) * 4. From 8651323ae9864ea78c0a7b8ab8b221c4ff95d76c Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Thu, 5 Mar 2026 12:08:16 +0100 Subject: [PATCH 20/23] CR Signed-off-by: Mateusz Front --- libs/estdlib/src/erlang.erl | 2 +- src/libAtomVM/context.c | 12 ++++---- src/libAtomVM/context.h | 10 +++---- src/libAtomVM/external_term.c | 2 ++ src/libAtomVM/globalcontext.h | 2 -- src/libAtomVM/nifs.c | 38 +++++++++--------------- src/libAtomVM/term.h | 55 +++++++---------------------------- 7 files changed, 36 insertions(+), 85 deletions(-) diff --git a/libs/estdlib/src/erlang.erl b/libs/estdlib/src/erlang.erl index c39461174e..bae702dd4e 100644 --- a/libs/estdlib/src/erlang.erl +++ b/libs/estdlib/src/erlang.erl @@ -1624,7 +1624,7 @@ raise(_Class, _Reason, _Stacktrace) -> %%----------------------------------------------------------------------------- %% @returns A reference aliasing the calling process. -%% @doc Creates an alias for the callling process. The alias can be used +%% @doc Creates an alias for the calling process. The alias can be used %% to send messages to the process like the PID. The alias can also be %% created along with a monitor - see `monitor/3'. The alias can be %% removed by calling `unalias/1'. diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index 8c79f0ac56..73ef804cf5 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -847,7 +847,7 @@ struct Monitor *monitor_link_new(term link_pid) } } -struct Monitor *monitor_new(term monitor_pid, RefData ref_data, bool is_monitoring) +struct Monitor *monitor_new(term monitor_pid, RefData *ref_data, bool is_monitoring) { struct MonitorLocalMonitor *monitor = malloc(sizeof(struct MonitorLocalMonitor)); if (IS_NULL_PTR(monitor)) { @@ -859,12 +859,12 @@ struct Monitor *monitor_new(term monitor_pid, RefData ref_data, bool is_monitori monitor->monitor.monitor_type = CONTEXT_MONITOR_MONITORED_LOCAL; } monitor->monitor_obj = monitor_pid; - monitor->ref_data = ref_data; + monitor->ref_data = *ref_data; return &monitor->monitor; } -struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, RefData ref_data) +struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, RefData *ref_data) { struct MonitorLocalRegisteredNameMonitor *monitor = malloc(sizeof(struct MonitorLocalRegisteredNameMonitor)); if (IS_NULL_PTR(monitor)) { @@ -873,19 +873,19 @@ struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, t monitor->monitor.monitor_type = CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME; monitor->monitor_process_id = monitor_process_id; monitor->monitor_name = monitor_name; - monitor->ref_data = ref_data; + monitor->ref_data = *ref_data; return &monitor->monitor; } -struct Monitor *monitor_alias_new(RefData ref_data, enum ContextMonitorAliasType alias_type) +struct Monitor *monitor_alias_new(RefData *ref_data, enum ContextMonitorAliasType alias_type) { struct MonitorAlias *monitor = malloc(sizeof(struct MonitorAlias)); if (IS_NULL_PTR(monitor)) { return NULL; } monitor->monitor.monitor_type = CONTEXT_MONITOR_ALIAS; - monitor->ref_data = ref_data; + monitor->ref_data = *ref_data; monitor->alias_type = alias_type; return &monitor->monitor; diff --git a/src/libAtomVM/context.h b/src/libAtomVM/context.h index 0320f1101d..34806018d4 100644 --- a/src/libAtomVM/context.h +++ b/src/libAtomVM/context.h @@ -530,9 +530,9 @@ struct Monitor *monitor_link_new(term link_pid); * @param is_monitoring if ctx is the monitoring process * @return the allocated monitor or NULL if allocation failed */ -struct Monitor *monitor_new(term monitor_pid, RefData ref_data, bool is_monitoring); +struct Monitor *monitor_new(term monitor_pid, RefData *ref_data, bool is_monitoring); -struct Monitor *monitor_alias_new(RefData ref_data, enum ContextMonitorAliasType alias_type); +struct Monitor *monitor_alias_new(RefData *ref_data, enum ContextMonitorAliasType alias_type); /** * @brief Create a monitor on a process by registered name. @@ -542,7 +542,7 @@ struct Monitor *monitor_alias_new(RefData ref_data, enum ContextMonitorAliasType * @param ref_data reference of the monitor * @return the allocated monitor or NULL if allocation failed */ -struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, RefData ref_data); +struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, RefData *ref_data); /** * @brief Create a resource monitor. @@ -601,17 +601,15 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks); /** * @brief Find a process alias - * @details Called within the process only * * @param ctx the context being executed - * @param ref_ticks reference of the alias to remove + * @param ref_ticks reference of the alias to find * @return found alias or NULL */ struct MonitorAlias *context_find_alias(Context *ctx, uint64_t ref_ticks); /** * @brief Remove an alias of a process - * @details Called within the process only * * @param alias The alias to remove, can be obtained using context_find_alias */ diff --git a/src/libAtomVM/external_term.c b/src/libAtomVM/external_term.c index 8f0e92c18e..8c2db94794 100644 --- a/src/libAtomVM/external_term.c +++ b/src/libAtomVM/external_term.c @@ -1366,6 +1366,8 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini if (external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) { if (len == 2) { heap_size = TERM_BOXED_REFERENCE_SHORT_SIZE; + } else if (len == 3) { + heap_size = TERM_BOXED_REFERENCE_PROCESS_SIZE; } else if (len == 4) { heap_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE; } diff --git a/src/libAtomVM/globalcontext.h b/src/libAtomVM/globalcontext.h index ff2a63a4cd..eed831f64d 100644 --- a/src/libAtomVM/globalcontext.h +++ b/src/libAtomVM/globalcontext.h @@ -46,8 +46,6 @@ extern "C" { #endif -#define INVALID_PROCESS_ID 0 - struct Context; #ifndef TYPEDEF_CONTEXT diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 4103cef687..033ad6f7bd 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -1440,29 +1440,25 @@ static term do_spawn(Context *ctx, Context *new_ctx, size_t arity, size_t n_free context_destroy(new_ctx); return term_invalid_term(); } - ref_data.ref_ticks = globalcontext_get_ref_ticks(ctx->global); struct Monitor *alias_monitor = NULL; if (is_alias) { - ref_data = (RefData){ - .type = RefTypeProcess, - .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } - }; - alias_monitor = monitor_alias_new(ref_data, alias_type); + ref_data = (RefData){ .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id }; + alias_monitor = monitor_alias_new(&ref_data, alias_type); if (IS_NULL_PTR(alias_monitor)) { context_destroy(new_ctx); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } } else { - ref_data = (RefData){ .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + ref_data = (RefData){ .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = INVALID_PROCESS_ID }; } - struct Monitor *new_monitor = monitor_new(term_from_local_process_id(ctx->process_id), ref_data, false); + struct Monitor *new_monitor = monitor_new(term_from_local_process_id(ctx->process_id), &ref_data, false); if (IS_NULL_PTR(new_monitor)) { context_destroy(new_ctx); free(alias_monitor); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - struct Monitor *self_monitor = monitor_new(new_pid, ref_data, true); + struct Monitor *self_monitor = monitor_new(new_pid, &ref_data, true); if (IS_NULL_PTR(self_monitor)) { free(alias_monitor); free(new_monitor); @@ -4485,23 +4481,20 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RefData ref_data; struct Monitor *alias_monitor = NULL; if (is_alias) { - ref_data = (RefData){ - .type = RefTypeProcess, - .process = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id } - }; - alias_monitor = monitor_alias_new(ref_data, alias_type); + ref_data = (RefData){ .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id }; + alias_monitor = monitor_alias_new(&ref_data, alias_type); if (IS_NULL_PTR(alias_monitor)) { globalcontext_get_process_unlock(ctx->global, target); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } } else { - ref_data = (RefData){ .type = RefTypeShort, .ref_ticks = globalcontext_get_ref_ticks(ctx->global) }; + ref_data = (RefData){ .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = INVALID_PROCESS_ID }; } struct Monitor *self_monitor; if (term_is_atom(target_proc)) { - self_monitor = monitor_registeredname_monitor_new(local_process_id, target_proc, ref_data); + self_monitor = monitor_registeredname_monitor_new(local_process_id, target_proc, &ref_data); } else { - self_monitor = monitor_new(target_pid, ref_data, true); + self_monitor = monitor_new(target_pid, &ref_data, true); } if (IS_NULL_PTR(self_monitor)) { globalcontext_get_process_unlock(ctx->global, target); @@ -4509,7 +4502,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } term monitoring_pid = term_from_local_process_id(ctx->process_id); - struct Monitor *other_monitor = monitor_new(monitoring_pid, ref_data, false); + struct Monitor *other_monitor = monitor_new(monitoring_pid, &ref_data, false); if (IS_NULL_PTR(other_monitor)) { free(alias_monitor); free(self_monitor); @@ -6695,14 +6688,9 @@ static term nif_erlang_alias(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - RefData ref_data = { - .type = RefTypeProcess, - .process = { - .ref_ticks = globalcontext_get_ref_ticks(ctx->global), - .process_id = ctx->process_id } - }; + RefData ref_data = { .ref_ticks = globalcontext_get_ref_ticks(ctx->global), .process_id = ctx->process_id }; term process_ref = term_from_ref_data(&ref_data, &ctx->heap); - struct Monitor *monitor = monitor_alias_new(ref_data, ContextMonitorAliasExplicitUnalias); + struct Monitor *monitor = monitor_alias_new(&ref_data, ContextMonitorAliasExplicitUnalias); if (IS_NULL_PTR(monitor)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index 0411900a44..b3d531eb07 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -191,10 +191,10 @@ _Static_assert(TERM_BOXED_REFERENCE_PROCESS_SIZE <= TERM_BOXED_REFERENCE_MAX_SIZ #define REFERENCE_RESOURCE_CONS_OFFSET 2 #if TERM_BYTES == 4 -#define REFERENCE_PROCESS_PID_OFFSET 2 +#define REFERENCE_PROCESS_PID_OFFSET 3 #elif TERM_BYTES == 8 -#define REFERENCE_PROCESS_PID_OFFSET 1 +#define REFERENCE_PROCESS_PID_OFFSET 2 #endif #define LIST_SIZE(num_elements, element_size) ((num_elements) * ((element_size) + CONS_SIZE)) @@ -205,6 +205,8 @@ _Static_assert(TERM_BOXED_REFERENCE_PROCESS_SIZE <= TERM_BOXED_REFERENCE_MAX_SIZ #define LIST_HEAD_INDEX 1 #define LIST_TAIL_INDEX 0 +#define INVALID_PROCESS_ID 0 + #define TERM_BINARY_SIZE_IS_HEAP(size) ((size) < REFC_BINARY_MIN) #if TERM_BYTES == 4 @@ -280,31 +282,11 @@ enum RefType RefTypeExternal }; -struct ProcessRefData -{ - uint64_t ref_ticks; - int32_t process_id; -}; - -struct ExternalRefData -{ - term node; - uint32_t creation; - uint16_t len; - const uint32_t *words; -}; - typedef struct RefData RefData; struct RefData { - enum RefType type; - union - { - uint64_t ref_ticks; - struct ProcessRefData process; - void *resource; - struct ExternalRefData external; - }; + uint64_t ref_ticks; + int32_t process_id; }; typedef struct PrinterFun PrinterFun; @@ -3124,27 +3106,10 @@ static inline term term_from_resource(void *resource, Heap *heap) static inline term term_from_ref_data(RefData *ref_data, Heap *heap) { - switch (ref_data->type) { - case RefTypeShort: { - return term_from_ref_ticks(ref_data->ref_ticks, heap); - } - case RefTypeProcess: { - return term_make_process_reference(ref_data->process.process_id, ref_data->ref_ticks, heap); - } - case RefTypeResource: { - return term_from_resource(ref_data->resource, heap); - } - case RefTypeExternal: { - return term_make_external_reference( - ref_data->external.node, - ref_data->external.len, - ref_data->external.words, - ref_data->external.creation, - heap); - } - default: { - UNREACHABLE(); - } + if (ref_data->process_id == INVALID_PROCESS_ID) { + return term_from_ref_ticks(ref_data->ref_ticks, heap); + } else { + return term_make_process_reference(ref_data->process_id, ref_data->ref_ticks, heap); } } From 12326355e1775e06813a3701e8653c9e45e9682d Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Thu, 5 Mar 2026 12:23:33 +0100 Subject: [PATCH 21/23] fix leaked git conflict Signed-off-by: Mateusz Front --- src/platforms/esp32/components/avm_builtins/network_driver.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/platforms/esp32/components/avm_builtins/network_driver.c b/src/platforms/esp32/components/avm_builtins/network_driver.c index 61369a7c69..b02bbf678d 100644 --- a/src/platforms/esp32/components/avm_builtins/network_driver.c +++ b/src/platforms/esp32/components/avm_builtins/network_driver.c @@ -116,10 +116,7 @@ struct ClientData uint32_t port_process_id; uint32_t owner_process_id; uint64_t ref_ticks; -<<<<<<< HEAD bool managed; -======= ->>>>>>> f036397d (Revert ref_data back to ref_ticks in drivers) }; static inline term make_atom(GlobalContext *global, AtomString atom_str) From d992defc224f5bee1e69eadab86da631558ae6ce Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Thu, 5 Mar 2026 14:23:34 +0100 Subject: [PATCH 22/23] Adjust freeing alias monitors Signed-off-by: Mateusz Front --- src/libAtomVM/context.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index 73ef804cf5..c94c7d59a7 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -805,7 +805,8 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx) break; } case CONTEXT_MONITOR_ALIAS: { - free(monitor); + struct MonitorAlias *alias = CONTAINER_OF(monitor, struct MonitorAlias, monitor); + free(alias); break; } } @@ -1150,7 +1151,7 @@ void context_unalias(struct MonitorAlias *alias) TERM_DEBUG_ASSERT(alias != NULL); struct Monitor *monitor = &alias->monitor; list_remove(&monitor->monitor_list_head); - free(monitor); + free(alias); } term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitoring) From 3b126b0f4e07764bf5d69bb0ad4dca7c7b4f9c73 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Tue, 10 Mar 2026 17:42:07 +0100 Subject: [PATCH 23/23] Use short reference for monitors of dead processes Signed-off-by: Mateusz Front --- src/libAtomVM/nifs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 033ad6f7bd..6f7f7a806e 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -4456,7 +4456,7 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global); - term ref = term_make_process_reference(ctx->process_id, ref_ticks, &ctx->heap); + term ref = term_from_ref_ticks(ref_ticks, &ctx->heap); term down_message_tuple = term_alloc_tuple(5, &ctx->heap); term_put_tuple_element(down_message_tuple, 0, DOWN_ATOM); term_put_tuple_element(down_message_tuple, 1, ref);