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}.

%%-----------------------------------------------------------------------------
%% @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();
Copy link
Collaborator

Choose a reason for hiding this comment

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

monitor can be also used with atoms or with tuples for processes on remote nodes.
I suggest introducing monitor_process_identifier() as Erlang does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is copy-pasted from the typespec above, I think it should be fixed in a separate PR

(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).
103 changes: 84 additions & 19 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 @@ -427,7 +428,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(monitor);
Expand All @@ -438,7 +439,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);

Expand Down Expand Up @@ -714,7 +715,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(monitor);
break;
Expand All @@ -726,7 +727,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(monitor);
break;
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_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");
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_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
Expand All @@ -799,6 +800,10 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx)
free(monitor);
break;
}
case CONTEXT_MONITOR_ALIAS: {
free(monitor);
break;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We may want a break here line what was there on line 800.

}
}
return result;
Expand Down Expand Up @@ -838,7 +843,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)) {
Expand All @@ -850,12 +855,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)) {
Expand All @@ -864,7 +869,20 @@ 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(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->alias_type = alias_type;

return &monitor->monitor;
}
Expand Down Expand Up @@ -903,7 +921,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_monitor);
return false;
}
Expand All @@ -914,7 +932,17 @@ 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_monitor);
return false;
}
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_data.ref_ticks == new_alias_monitor->ref_data.ref_ticks)) {
free(new_monitor);
return false;
}
Expand Down Expand Up @@ -1053,14 +1081,19 @@ 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);
switch (monitor->monitor_type) {
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(monitor);
return;
Expand All @@ -1069,7 +1102,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(monitor);
return;
Expand All @@ -1086,11 +1119,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_data.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 @@ -1100,15 +1158,15 @@ 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;
}
break;
}
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);
}
Expand All @@ -1117,6 +1175,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 @@ -1242,15 +1301,21 @@ 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 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;
}
case CONTEXT_MONITOR_MONITORED_LOCAL: {
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;
}
Expand All @@ -1260,7 +1325,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;
}
Expand Down
Loading
Loading