Skip to content

Commit 1bdef14

Browse files
committed
Process aliases
Signed-off-by: Mateusz Front <[email protected]>
1 parent 949bf85 commit 1bdef14

File tree

13 files changed

+627
-38
lines changed

13 files changed

+627
-38
lines changed

libs/estdlib/src/erlang.erl

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
make_ref/0,
100100
send/2,
101101
monitor/2,
102+
monitor/3,
102103
demonitor/1,
103104
demonitor/2,
104105
exit/1,
@@ -133,7 +134,9 @@
133134
dist_ctrl_put_data/2,
134135
unique_integer/0,
135136
unique_integer/1,
136-
raise/3
137+
raise/3,
138+
alias/0,
139+
unalias/1
137140
]).
138141

139142
-export_type([
@@ -177,7 +180,8 @@
177180
| {max_heap_size, pos_integer()}
178181
| {atomvm_heap_growth, atomvm_heap_growth_strategy()}
179182
| link
180-
| monitor.
183+
| monitor
184+
| {monitor, [monitor_option()]}.
181185

182186
-type send_destination() ::
183187
pid()
@@ -203,6 +207,8 @@
203207
-type raise_stacktrace() ::
204208
[{module(), atom(), arity() | [term()]} | {function(), arity() | [term()]}] | stacktrace().
205209

210+
-type monitor_option() :: {'alias', 'explicit_unalias' | 'demonitor' | 'reply_demonitor'}.
211+
206212
%%-----------------------------------------------------------------------------
207213
%% @param Time time in milliseconds after which to send the timeout message.
208214
%% @param Dest Pid or server name to which to send the timeout message.
@@ -1167,6 +1173,28 @@ send(_Target, _Message) ->
11671173
monitor(_Type, _PidOrPort) ->
11681174
erlang:nif_error(undefined).
11691175

1176+
%%-----------------------------------------------------------------------------
1177+
%% @param Type type of monitor to create
1178+
%% @param PidOrPort pid or port of the object to monitor
1179+
%% @param Options monitor options
1180+
%% @returns a monitor reference
1181+
%% @doc Creates a monitor and allows passing additional options.
1182+
%% Currently, only the `{alias, AliasMode}' option is supported. Passing it
1183+
%% makes the monitor also an alias on the calling process (see `alias/0').
1184+
%% `AliasMode' defines the behaviour of the alias:
1185+
%% - explicit_unalias - the alias can be only removed with `unalias/1',
1186+
%% - demonitor - the alias is also removed when `demonitor/1' is called
1187+
%% on the monitor,
1188+
%% - reply_demonitor - the alias is also removed after a first message
1189+
%% is sent via it.
1190+
%% @end
1191+
%%-----------------------------------------------------------------------------
1192+
-spec monitor
1193+
(Type :: process, Pid :: pid() | atom(), [monitor_option()]) -> reference();
1194+
(Type :: port, Port :: port() | atom(), [monitor_option()]) -> reference().
1195+
monitor(_Type, _PidOrPort, _Options) ->
1196+
erlang:nif_error(undefined).
1197+
11701198
%%-----------------------------------------------------------------------------
11711199
%% @param Monitor reference of monitor to remove
11721200
%% @returns `true'
@@ -1578,3 +1606,25 @@ nif_error(_Reason) ->
15781606
no_return().
15791607
raise(_Class, _Reason, _Stacktrace) ->
15801608
erlang:nif_error(undefined).
1609+
1610+
%%-----------------------------------------------------------------------------
1611+
%% @returns A reference aliasing the calling process.
1612+
%% @doc Creates an alias for the callling process. The alias can be used
1613+
%% to send messages to the process like the PID. The alias can also be
1614+
%% created along with a monitor - see `monitor/3'. The alias can be
1615+
%% removed by calling `unalias/1'.
1616+
%% @end
1617+
%%-----------------------------------------------------------------------------
1618+
-spec alias() -> Alias when Alias :: reference().
1619+
alias() ->
1620+
erlang:nif_error(undefined).
1621+
1622+
%%-----------------------------------------------------------------------------
1623+
%% @param Alias the alias to be removed.
1624+
%% @returns `true' if alias was removed, `false' if it was not found
1625+
%% @doc Removes process alias. See `alias/0' for more information.
1626+
%% @end
1627+
%%-----------------------------------------------------------------------------
1628+
-spec unalias(Alias) -> boolean() when Alias :: reference().
1629+
unalias(_Alias) ->
1630+
erlang:nif_error(undefined).

src/libAtomVM/context.c

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ void context_destroy(Context *ctx)
260260
case CONTEXT_MONITOR_MONITORED_LOCAL:
261261
case CONTEXT_MONITOR_MONITORING_LOCAL:
262262
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME:
263+
case CONTEXT_MONITOR_ALIAS:
263264
UNREACHABLE();
264265
}
265266
}
@@ -774,15 +775,15 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx)
774775
Context *target = globalcontext_get_process_nolock(glb, local_process_id);
775776
// Target cannot be NULL as we processed Demonitor signals
776777
assert(target != NULL);
777-
int required_terms = REF_SIZE + TUPLE_SIZE(5);
778+
int required_terms = TERM_BOXED_PROCESS_REF_SIZE + TUPLE_SIZE(5);
778779
if (UNLIKELY(memory_ensure_free(ctx, required_terms) != MEMORY_GC_OK)) {
779780
// TODO: handle out of memory here
780781
fprintf(stderr, "Cannot handle out of memory.\n");
781782
globalcontext_get_process_unlock(glb, target);
782783
AVM_ABORT();
783784
}
784785
// Prepare the message on ctx's heap which will be freed afterwards.
785-
term ref = term_from_ref_ticks(monitored_monitor->ref_ticks, &ctx->heap);
786+
term ref = term_make_process_reference(target->process_id, monitored_monitor->ref_ticks, &ctx->heap);
786787

787788
term port_or_process = term_pid_or_port_from_context(ctx);
788789
term port_or_process_atom
@@ -799,6 +800,9 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx)
799800
free(monitor);
800801
break;
801802
}
803+
case CONTEXT_MONITOR_ALIAS: {
804+
free(monitor);
805+
}
802806
}
803807
}
804808
return result;
@@ -869,6 +873,19 @@ struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, t
869873
return &monitor->monitor;
870874
}
871875

876+
struct Monitor *monitor_alias_new(uint64_t ref_ticks, enum ContextMonitorAliasType alias_type)
877+
{
878+
struct MonitorAlias *monitor = malloc(sizeof(struct MonitorAlias));
879+
if (IS_NULL_PTR(monitor)) {
880+
return NULL;
881+
}
882+
monitor->monitor.monitor_type = CONTEXT_MONITOR_ALIAS;
883+
monitor->ref_ticks = ref_ticks;
884+
monitor->alias_type = alias_type;
885+
886+
return &monitor->monitor;
887+
}
888+
872889
struct Monitor *monitor_resource_monitor_new(void *resource, uint64_t ref_ticks)
873890
{
874891
struct ResourceContextMonitor *monitor = malloc(sizeof(struct ResourceContextMonitor));
@@ -920,6 +937,16 @@ bool context_add_monitor(Context *ctx, struct Monitor *new_monitor)
920937
}
921938
break;
922939
}
940+
case CONTEXT_MONITOR_ALIAS: {
941+
struct MonitorAlias *new_alias_monitor = CONTAINER_OF(new_monitor, struct MonitorAlias, monitor);
942+
struct MonitorAlias *existing_alias_monitor = CONTAINER_OF(existing, struct MonitorAlias, monitor);
943+
944+
if (UNLIKELY(existing_alias_monitor->alias_type == new_alias_monitor->alias_type && existing_alias_monitor->ref_ticks == new_alias_monitor->ref_ticks)) {
945+
free(new_monitor);
946+
return false;
947+
}
948+
break;
949+
}
923950
case CONTEXT_MONITOR_RESOURCE: {
924951
struct ResourceContextMonitor *new_resource_monitor = CONTAINER_OF(new_monitor, struct ResourceContextMonitor, monitor);
925952
struct ResourceContextMonitor *existing_resource_monitor = CONTAINER_OF(existing, struct ResourceContextMonitor, monitor);
@@ -1053,6 +1080,11 @@ void context_unlink_ack(Context *ctx, term link_pid, uint64_t unlink_id)
10531080

10541081
void context_demonitor(Context *ctx, uint64_t ref_ticks)
10551082
{
1083+
struct MonitorAlias *alias = context_find_alias(ctx, ref_ticks);
1084+
if (alias != NULL && alias->alias_type != ContextMonitorAliasExplicitUnalias) {
1085+
context_unalias(alias);
1086+
}
1087+
10561088
struct ListHead *item;
10571089
LIST_FOR_EACH (item, &ctx->monitors_head) {
10581090
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
@@ -1086,11 +1118,36 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks)
10861118
}
10871119
case CONTEXT_MONITOR_LINK_LOCAL:
10881120
case CONTEXT_MONITOR_LINK_REMOTE:
1121+
case CONTEXT_MONITOR_ALIAS:
10891122
break;
10901123
}
10911124
}
10921125
}
10931126

1127+
struct MonitorAlias *context_find_alias(Context *ctx, uint64_t ref_ticks)
1128+
{
1129+
struct ListHead *item;
1130+
LIST_FOR_EACH (item, &ctx->monitors_head) {
1131+
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
1132+
if (monitor->monitor_type == CONTEXT_MONITOR_ALIAS) {
1133+
struct MonitorAlias *alias_monitor = CONTAINER_OF(monitor, struct MonitorAlias, monitor);
1134+
if (alias_monitor->ref_ticks == ref_ticks) {
1135+
return alias_monitor;
1136+
}
1137+
}
1138+
}
1139+
1140+
return NULL;
1141+
}
1142+
1143+
void context_unalias(struct MonitorAlias *alias)
1144+
{
1145+
TERM_DEBUG_ASSERT(alias != NULL);
1146+
struct Monitor *monitor = &alias->monitor;
1147+
list_remove(&monitor->monitor_list_head);
1148+
free(monitor);
1149+
}
1150+
10941151
term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitoring)
10951152
{
10961153
struct ListHead *item;
@@ -1117,6 +1174,7 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori
11171174
case CONTEXT_MONITOR_LINK_LOCAL:
11181175
case CONTEXT_MONITOR_LINK_REMOTE:
11191176
case CONTEXT_MONITOR_RESOURCE:
1177+
case CONTEXT_MONITOR_ALIAS:
11201178
break;
11211179
}
11221180
}
@@ -1246,6 +1304,12 @@ COLD_FUNC void context_dump(Context *ctx)
12461304
fprintf(stderr, "\n");
12471305
break;
12481306
}
1307+
case CONTEXT_MONITOR_ALIAS: {
1308+
struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor);
1309+
fprintf(stderr, "has alias ref=%lu", (long unsigned) monitored_monitor->ref_ticks);
1310+
fprintf(stderr, "\n");
1311+
break;
1312+
}
12491313
case CONTEXT_MONITOR_MONITORED_LOCAL: {
12501314
struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor);
12511315
fprintf(stderr, "monitored by ");

src/libAtomVM/context.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ enum ContextMonitorType
172172
CONTEXT_MONITOR_RESOURCE,
173173
CONTEXT_MONITOR_LINK_REMOTE,
174174
CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME,
175+
CONTEXT_MONITOR_ALIAS,
176+
};
177+
178+
enum ContextMonitorAliasType
179+
{
180+
ContextMonitorAliasExplicitUnalias,
181+
ContextMonitorAliasDemonitor,
182+
ContextMonitorAliasReplyDemonitor,
175183
};
176184

177185
#define UNLINK_ID_LINK_ACTIVE 0x0
@@ -207,6 +215,13 @@ struct MonitorLocalRegisteredNameMonitor
207215
term monitor_name;
208216
};
209217

218+
struct MonitorAlias
219+
{
220+
struct Monitor monitor;
221+
uint64_t ref_ticks;
222+
enum ContextMonitorAliasType alias_type;
223+
};
224+
210225
// The other half is called ResourceMonitor and is a linked list of resources
211226
struct ResourceContextMonitor
212227
{
@@ -512,6 +527,8 @@ struct Monitor *monitor_link_new(term link_pid);
512527
*/
513528
struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monitoring);
514529

530+
struct Monitor *monitor_alias_new(uint64_t ref_ticks, enum ContextMonitorAliasType alias_type);
531+
515532
/**
516533
* @brief Create a monitor on a process by registered name.
517534
*
@@ -577,6 +594,24 @@ void context_unlink_ack(Context *ctx, term link_pid, uint64_t unlink_id);
577594
*/
578595
void context_demonitor(Context *ctx, uint64_t ref_ticks);
579596

597+
/**
598+
* @brief Find a process alias
599+
* @details Called within the process only
600+
*
601+
* @param ctx the context being executed
602+
* @param ref_ticks reference of the alias to remove
603+
* @return found alias or NULL
604+
*/
605+
struct MonitorAlias *context_find_alias(Context *ctx, uint64_t ref_ticks);
606+
607+
/**
608+
* @brief Remove an alias of a process
609+
* @details Called within the process only
610+
*
611+
* @param alias The alias to remove, can be obtained using context_find_alias
612+
*/
613+
void context_unalias(struct MonitorAlias *alias);
614+
580615
/**
581616
* @brief Get target of a monitor.
582617
*

src/libAtomVM/defaultatoms.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,9 @@ X(JIT_X86_64_ATOM, "\xA", "jit_x86_64")
211211
X(JIT_AARCH64_ATOM, "\xB", "jit_aarch64")
212212
X(JIT_ARMV6M_ATOM, "\xA", "jit_armv6m")
213213
X(JIT_RISCV32_ATOM, "\xB", "jit_riscv32")
214+
215+
X(ALIAS_ATOM, "\x5", "alias")
216+
X(DEMONITOR_ATOM, "\x9", "demonitor")
217+
X(EXPLICIT_UNALIAS_ATOM, "\x10", "explicit_unalias")
218+
X(REPLY_DEMONITOR_ATOM, "\xF", "reply_demonitor")
219+
X(TAG_ATOM, "\x3", "tag")

src/libAtomVM/externalterm.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb)
492492
uint32_t len;
493493
if (term_is_resource_reference(t)) {
494494
len = 4;
495+
} else if (term_is_process_reference(t)) {
496+
len = 3;
495497
} else {
496498
len = 2;
497499
}
@@ -513,6 +515,15 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb)
513515
WRITE_64_UNALIGNED(buf + k + 12, ((uintptr_t) serialize_ref));
514516
}
515517
return k + 20;
518+
} else if (term_is_process_reference(t)) {
519+
if (!IS_NULL_PTR(buf)) {
520+
uint64_t ticks = term_to_ref_ticks(t);
521+
uint32_t process_id = term_process_ref_to_process_id(t);
522+
WRITE_32_UNALIGNED(buf + k, creation);
523+
WRITE_64_UNALIGNED(buf + k + 4, ticks);
524+
WRITE_32_UNALIGNED(buf + k + 12, process_id);
525+
}
526+
return k + 16;
516527
} else {
517528
if (!IS_NULL_PTR(buf)) {
518529
uint64_t ticks = term_to_ref_ticks(t);
@@ -901,6 +912,10 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm
901912
if (len == 2 && node == this_node && creation == this_creation) {
902913
uint64_t ticks = ((uint64_t) data[0]) << 32 | data[1];
903914
return term_from_ref_ticks(ticks, heap);
915+
} else if (len == 3 && node == this_node && creation == this_creation) {
916+
uint64_t ticks = ((uint64_t) data[0]) << 32 | data[1];
917+
uint32_t process_id = data[2];
918+
return term_make_process_reference(process_id, ticks, heap);
904919
} else if (len == 4 && node == this_node && creation == this_creation) {
905920
// This is a resource
906921
uint64_t resource_type_ptr = ((uint64_t) data[0]) << 32 | data[1];

0 commit comments

Comments
 (0)