Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions libs/estdlib/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
make_ref/0,
send/2,
monitor/2,
monitor/3,
demonitor/1,
demonitor/2,
exit/1,
Expand Down Expand Up @@ -133,7 +134,9 @@
dist_ctrl_put_data/2,
unique_integer/0,
unique_integer/1,
raise/3
raise/3,
alias/0,
unalias/1
]).

-export_type([
Expand Down Expand Up @@ -177,7 +180,8 @@
| {max_heap_size, pos_integer()}
| {atomvm_heap_growth, atomvm_heap_growth_strategy()}
| link
| monitor.
| monitor
| {monitor, [monitor_option()]}.

-type send_destination() ::
pid()
Expand All @@ -203,6 +207,8 @@
-type raise_stacktrace() ::
[{module(), atom(), arity() | [term()]} | {function(), arity() | [term()]}] | stacktrace().

-type monitor_option() :: {'alias', 'explicit_unalias' | 'demonitor' | 'reply_demonitor'}.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not enforced by style guide but usually we don't quote atoms when it's not required.


%%-----------------------------------------------------------------------------
%% @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.
Expand Down Expand Up @@ -1167,6 +1173,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'
Expand Down Expand Up @@ -1578,3 +1606,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).
68 changes: 66 additions & 2 deletions src/libAtomVM/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,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();
}
}
Expand Down Expand Up @@ -774,15 +775,15 @@ 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");
globalcontext_get_process_unlock(glb, target);
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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not what OTP does and there probably is a good reason.
Regular monitors are smaller than aliases.

7> Pid = spawn(fun() -> receive ok -> ok end end).
<0.94.0>
8> monitor(process, Pid).
#Ref<0.3008598808.1200095248.57512>
9> monitor(process, Pid, [{alias, explicit_unalias}]).
#Ref<0.0.11651.3008598808.1200160784.57533>
10> make_ref().
#Ref<0.3008598808.1200095248.57553>


term port_or_process = term_pid_or_port_from_context(ctx);
term port_or_process_atom
Expand All @@ -799,6 +800,9 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx)
free(monitor);
break;
}
case CONTEXT_MONITOR_ALIAS: {
free(monitor);
}
}
}
return result;
Expand Down Expand Up @@ -869,6 +873,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));
Expand Down Expand Up @@ -920,6 +937,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);
Expand Down Expand Up @@ -1053,6 +1080,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);
Expand Down Expand Up @@ -1086,11 +1118,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;
Expand All @@ -1117,6 +1174,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;
}
}
Expand Down Expand Up @@ -1246,6 +1304,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 ");
Expand Down
35 changes: 35 additions & 0 deletions src/libAtomVM/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,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
Expand Down Expand Up @@ -207,6 +215,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
{
Expand Down Expand Up @@ -512,6 +527,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.
*
Expand Down Expand Up @@ -577,6 +594,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.
*
Expand Down
6 changes: 6 additions & 0 deletions src/libAtomVM/defaultatoms.def
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,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")
15 changes: 15 additions & 0 deletions src/libAtomVM/externalterm.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,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;
}
Expand All @@ -513,6 +515,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);
Expand Down Expand Up @@ -901,6 +912,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];
Expand Down
Loading
Loading