Skip to content

Commit 9299f6d

Browse files
committed
Implement monitoring process by registered name
Also simplify gen_server implementation when ServerRef is an atom Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 630c11d commit 9299f6d

File tree

5 files changed

+197
-55
lines changed

5 files changed

+197
-55
lines changed

libs/estdlib/src/gen_server.erl

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -387,20 +387,13 @@ stop(ServerRef) ->
387387
%%-----------------------------------------------------------------------------
388388
-spec stop(ServerRef :: server_ref(), Reason :: term(), Timeout :: non_neg_integer() | infinity) ->
389389
ok | {error, Reason :: term()}.
390-
stop(Name, Reason, Timeout) when is_atom(Name) ->
391-
case erlang:whereis(Name) of
392-
undefined ->
393-
{error, undefined};
394-
Pid when is_pid(Pid) ->
395-
stop(Pid, Reason, Timeout)
396-
end;
397-
stop(Pid, Reason, Timeout) when is_pid(Pid) ->
398-
MonitorRef = monitor(process, Pid),
390+
stop(ServerRef, Reason, Timeout) ->
391+
MonitorRef = monitor(process, ServerRef),
399392
Pid ! {'$stop', Reason},
400393
receive
401-
{'DOWN', MonitorRef, process, Pid, Reason} ->
394+
{'DOWN', MonitorRef, process, _, Reason} ->
402395
ok;
403-
{'DOWN', MonitorRef, process, Pid, AnotherReason} ->
396+
{'DOWN', MonitorRef, process, _, AnotherReason} ->
404397
erlang:exit(AnotherReason)
405398
after Timeout ->
406399
demonitor(MonitorRef, [flush]),
@@ -431,29 +424,22 @@ call(ServerRef, Request) ->
431424
%%-----------------------------------------------------------------------------
432425
-spec call(ServerRef :: server_ref(), Request :: term(), TimeoutMs :: timeout()) ->
433426
Reply :: term() | {error, Reason :: term()}.
434-
call(Name, Request, TimeoutMs) when is_atom(Name) ->
435-
case erlang:whereis(Name) of
436-
undefined ->
437-
erlang:exit({noproc, {?MODULE, ?FUNCTION_NAME, [Name, Request]}});
438-
Pid when is_pid(Pid) ->
439-
call(Pid, Request, TimeoutMs)
440-
end;
441-
call(Pid, Request, TimeoutMs) when is_pid(Pid) ->
442-
MonitorRef = monitor(process, Pid),
443-
Pid ! {'$gen_call', {self(), MonitorRef}, Request},
427+
call(ServerRef, Request, TimeoutMs) ->
428+
MonitorRef = monitor(process, ServerRef),
429+
ServerRef ! {'$gen_call', {self(), MonitorRef}, Request},
444430
receive
445-
{'DOWN', MonitorRef, process, Pid, {E, []} = _Reason} ->
446-
erlang:exit({E, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}});
447-
{'DOWN', MonitorRef, process, Pid, {_E, _L} = Reason} ->
431+
{'DOWN', MonitorRef, process, _, {E, []} = _Reason} ->
432+
erlang:exit({E, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}});
433+
{'DOWN', MonitorRef, process, _, {_E, _L} = Reason} ->
448434
erlang:exit(Reason);
449-
{'DOWN', MonitorRef, process, Pid, Atom} when is_atom(Atom) ->
450-
erlang:exit({Atom, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}});
435+
{'DOWN', MonitorRef, process, _, Atom} when is_atom(Atom) ->
436+
erlang:exit({Atom, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}});
451437
{MonitorRef, Reply} ->
452438
demonitor(MonitorRef, [flush]),
453439
Reply
454440
after TimeoutMs ->
455441
demonitor(MonitorRef, [flush]),
456-
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}})
442+
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}})
457443
end.
458444

459445
%%-----------------------------------------------------------------------------
@@ -467,15 +453,8 @@ call(Pid, Request, TimeoutMs) when is_pid(Pid) ->
467453
%% @end
468454
%%-----------------------------------------------------------------------------
469455
-spec cast(ServerRef :: server_ref(), Request :: term()) -> ok | {error, Reason :: term()}.
470-
cast(Name, Request) when is_atom(Name) ->
471-
case erlang:whereis(Name) of
472-
undefined ->
473-
{error, undefined};
474-
Pid when is_pid(Pid) ->
475-
cast(Pid, Request)
476-
end;
477-
cast(Pid, Request) when is_pid(Pid) ->
478-
Pid ! {'$gen_cast', Request},
456+
cast(ServerRef, Request) ->
457+
ServerRef ! {'$gen_cast', Request},
479458
ok.
480459

481460
%%-----------------------------------------------------------------------------

src/libAtomVM/context.c

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "globalcontext.h"
3131
#include "list.h"
3232
#include "mailbox.h"
33+
#include "memory.h"
3334
#include "smp.h"
3435
#include "synclist.h"
3536
#include "sys.h"
@@ -252,6 +253,7 @@ void context_destroy(Context *ctx)
252253
case CONTEXT_MONITOR_LINK_LOCAL:
253254
case CONTEXT_MONITOR_MONITORED_LOCAL:
254255
case CONTEXT_MONITOR_MONITORING_LOCAL:
256+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME:
255257
UNREACHABLE();
256258
}
257259
}
@@ -418,15 +420,34 @@ void context_process_monitor_down_signal(Context *ctx, struct TermSignal *signal
418420
LIST_FOR_EACH (item, &ctx->monitors_head) {
419421
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
420422
if (monitor->monitor_type == CONTEXT_MONITOR_MONITORING_LOCAL) {
421-
struct MonitorLocalMonitor *monitored_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor);
422-
if (monitored_monitor->monitor_obj == monitor_obj && monitored_monitor->ref_ticks == ref_ticks) {
423+
struct MonitorLocalMonitor *monitoring_monitor = CONTAINER_OF(monitor, struct MonitorLocalMonitor, monitor);
424+
if (monitoring_monitor->monitor_obj == monitor_obj && monitoring_monitor->ref_ticks == ref_ticks) {
423425
// Remove link
424426
list_remove(&monitor->monitor_list_head);
425427
free(monitor);
426428
// Enqueue the term as a message.
427429
mailbox_send(ctx, signal->signal_term);
428430
break;
429431
}
432+
} else if (monitor->monitor_type == CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME) {
433+
int32_t monitor_process_id = term_to_local_process_id(monitor_obj);
434+
struct MonitorLocalRegisteredNameMonitor *monitoring_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
435+
if (monitoring_monitor->monitor_process_id == monitor_process_id && monitoring_monitor->ref_ticks == ref_ticks) {
436+
// Remove link
437+
list_remove(&monitor->monitor_list_head);
438+
439+
// We need to modify the monitor_obj item
440+
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2), temp_heap)
441+
term name_tuple = term_alloc_tuple(2, &temp_heap);
442+
term_put_tuple_element(name_tuple, 0, monitoring_monitor->monitor_name);
443+
term_put_tuple_element(name_tuple, 1, ctx->global->node_name);
444+
term_put_tuple_element(signal->signal_term, 3, name_tuple);
445+
mailbox_send(ctx, signal->signal_term);
446+
END_WITH_STACK_HEAP(temp_heap, ctx->global);
447+
448+
free(monitor);
449+
break;
450+
}
430451
}
431452
}
432453
// If monitor was not found, it was removed and message should not be sent.
@@ -623,6 +644,18 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx)
623644
free(monitor);
624645
break;
625646
}
647+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
648+
// We are the monitoring process.
649+
struct MonitorLocalRegisteredNameMonitor *monitoring_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
650+
int32_t local_process_id = monitoring_monitor->monitor_process_id;
651+
Context *target = globalcontext_get_process_nolock(glb, local_process_id);
652+
if (LIKELY(target != NULL)) {
653+
// target can be null if we didn't process a MonitorDownSignal
654+
mailbox_send_ref_signal(target, DemonitorSignal, monitoring_monitor->ref_ticks);
655+
}
656+
free(monitor);
657+
break;
658+
}
626659
case CONTEXT_MONITOR_LINK_LOCAL: {
627660
struct LinkLocalMonitor *link_monitor = CONTAINER_OF(monitor, struct LinkLocalMonitor, monitor);
628661
// Handle the case of inactive link.
@@ -747,6 +780,20 @@ struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monito
747780
return &monitor->monitor;
748781
}
749782

783+
struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, uint64_t ref_ticks)
784+
{
785+
struct MonitorLocalRegisteredNameMonitor *monitor = malloc(sizeof(struct MonitorLocalRegisteredNameMonitor));
786+
if (IS_NULL_PTR(monitor)) {
787+
return NULL;
788+
}
789+
monitor->monitor.monitor_type = CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME;
790+
monitor->monitor_process_id = monitor_process_id;
791+
monitor->monitor_name = monitor_name;
792+
monitor->ref_ticks = ref_ticks;
793+
794+
return &monitor->monitor;
795+
}
796+
750797
struct Monitor *monitor_resource_monitor_new(void *resource, uint64_t ref_ticks)
751798
{
752799
struct ResourceContextMonitor *monitor = malloc(sizeof(struct ResourceContextMonitor));
@@ -786,6 +833,17 @@ bool context_add_monitor(Context *ctx, struct Monitor *new_monitor)
786833
}
787834
break;
788835
}
836+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
837+
struct MonitorLocalRegisteredNameMonitor *new_local_registeredname_monitor = CONTAINER_OF(new_monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
838+
struct MonitorLocalRegisteredNameMonitor *existing_local_registeredname_monitor = CONTAINER_OF(existing, struct MonitorLocalRegisteredNameMonitor, monitor);
839+
if (UNLIKELY(existing_local_registeredname_monitor->monitor_process_id == new_local_registeredname_monitor->monitor_process_id
840+
&& existing_local_registeredname_monitor->monitor_name == new_local_registeredname_monitor->monitor_name
841+
&& existing_local_registeredname_monitor->ref_ticks == new_local_registeredname_monitor->ref_ticks)) {
842+
free(new_monitor);
843+
return false;
844+
}
845+
break;
846+
}
789847
case CONTEXT_MONITOR_RESOURCE: {
790848
struct ResourceContextMonitor *new_resource_monitor = CONTAINER_OF(new_monitor, struct ResourceContextMonitor, monitor);
791849
struct ResourceContextMonitor *existing_resource_monitor = CONTAINER_OF(existing, struct ResourceContextMonitor, monitor);
@@ -933,6 +991,15 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks)
933991
}
934992
break;
935993
}
994+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
995+
struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
996+
if (local_registeredname_monitor->ref_ticks == ref_ticks) {
997+
list_remove(&monitor->monitor_list_head);
998+
free(monitor);
999+
return;
1000+
}
1001+
break;
1002+
}
9361003
case CONTEXT_MONITOR_RESOURCE: {
9371004
struct ResourceContextMonitor *resource_monitor = CONTAINER_OF(monitor, struct ResourceContextMonitor, monitor);
9381005
if (resource_monitor->ref_ticks == ref_ticks) {
@@ -963,6 +1030,14 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori
9631030
}
9641031
break;
9651032
}
1033+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
1034+
struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
1035+
if (local_registeredname_monitor->ref_ticks == ref_ticks) {
1036+
*is_monitoring = true;
1037+
return term_from_local_process_id(local_registeredname_monitor->monitor_process_id);
1038+
}
1039+
break;
1040+
}
9661041
case CONTEXT_MONITOR_LINK_LOCAL:
9671042
case CONTEXT_MONITOR_LINK_REMOTE:
9681043
case CONTEXT_MONITOR_RESOURCE:
@@ -1103,6 +1178,16 @@ COLD_FUNC void context_dump(Context *ctx)
11031178
fprintf(stderr, "\n");
11041179
break;
11051180
}
1181+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
1182+
struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
1183+
fprintf(stderr, "monitor to ");
1184+
term_display(stderr, local_registeredname_monitor->monitor_name, ctx);
1185+
fprintf(stderr, " (");
1186+
term_display(stderr, term_from_local_process_id(local_registeredname_monitor->monitor_process_id), ctx);
1187+
fprintf(stderr, ") ref=%lu", (long unsigned) local_registeredname_monitor->ref_ticks);
1188+
fprintf(stderr, "\n");
1189+
break;
1190+
}
11061191
case CONTEXT_MONITOR_RESOURCE: {
11071192
struct ResourceContextMonitor *resource_monitor = CONTAINER_OF(monitor, struct ResourceContextMonitor, monitor);
11081193
fprintf(stderr, "monitored by resource %p ref=%lu", resource_monitor->resource_obj, (long unsigned) resource_monitor->ref_ticks);

src/libAtomVM/context.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ enum ContextMonitorType
159159
CONTEXT_MONITOR_MONITORED_LOCAL,
160160
CONTEXT_MONITOR_RESOURCE,
161161
CONTEXT_MONITOR_LINK_REMOTE,
162+
CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME,
162163
};
163164

164165
#define UNLINK_ID_LINK_ACTIVE 0x0
@@ -186,6 +187,14 @@ struct MonitorLocalMonitor
186187
term monitor_obj;
187188
};
188189

190+
struct MonitorLocalRegisteredNameMonitor
191+
{
192+
struct Monitor monitor;
193+
uint64_t ref_ticks;
194+
int32_t monitor_process_id;
195+
term monitor_name;
196+
};
197+
189198
// The other half is called ResourceMonitor and is a linked list of resources
190199
struct ResourceContextMonitor
191200
{
@@ -477,13 +486,24 @@ struct Monitor *monitor_link_new(term link_pid);
477486
/**
478487
* @brief Create a monitor on a process.
479488
*
480-
* @param monitor_pid monitoring process
489+
* @param monitor_pid monitored process
481490
* @param ref_ticks reference of the monitor
482491
* @param is_monitoring if ctx is the monitoring process
483492
* @return the allocated monitor or NULL if allocation failed
484493
*/
485494
struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monitoring);
486495

496+
/**
497+
* @brief Create a monitor on a process by registered name.
498+
*
499+
* @param monitor_process_id monitored process id
500+
* @param monitor_name name of the monitor (atom)
501+
* @param ref_ticks reference of the monitor
502+
* @param is_monitoring if ctx is the monitoring process
503+
* @return the allocated monitor or NULL if allocation failed
504+
*/
505+
struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, uint64_t ref_ticks);
506+
487507
/**
488508
* @brief Create a resource monitor.
489509
*
@@ -545,8 +565,8 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks);
545565
* @param ctx the context being executed
546566
* @param ref_ticks reference of the monitor to remove
547567
* @param is_monitoring whether ctx is the monitoring process.
548-
* @return pid of monitoring process, self() if process is monitoring (and not
549-
* monitored) or term_invalid() if no monitor could be found.
568+
* @return pid of monitored or monitoring process or term_invalid()
569+
* if no monitor could be found.
550570
*/
551571
term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitoring);
552572

src/libAtomVM/nifs.c

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4133,28 +4133,43 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[])
41334133
UNUSED(argc);
41344134

41354135
term object_type = argv[0];
4136-
term target_pid = argv[1];
4136+
term target_proc = argv[1];
4137+
term target_pid;
4138+
size_t target_proc_size = 0;
41374139

41384140
if (object_type != PROCESS_ATOM && object_type != PORT_ATOM) {
41394141
RAISE_ERROR(BADARG_ATOM);
41404142
}
41414143

4142-
VALIDATE_VALUE(target_pid, term_is_local_pid_or_port);
4144+
if (term_is_atom(target_proc)) {
4145+
target_pid = globalcontext_get_registered_process(ctx->global, term_to_atom_index(target_proc));
4146+
target_proc_size = TUPLE_SIZE(2);
4147+
} else {
4148+
VALIDATE_VALUE(target_proc, term_is_local_pid_or_port);
4149+
target_pid = target_proc;
4150+
}
41434151

4144-
int local_process_id = term_to_local_process_id(target_pid);
4145-
// Monitoring self is possible but no monitor is actually created
4146-
if (UNLIKELY(local_process_id == ctx->process_id)) {
4147-
if (UNLIKELY(memory_ensure_free_opt(ctx, REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
4148-
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
4152+
Context *target;
4153+
int32_t local_process_id;
4154+
if (UNLIKELY(target_pid == UNDEFINED_ATOM)) {
4155+
target = NULL;
4156+
} else {
4157+
local_process_id = term_to_local_process_id(target_pid);
4158+
// Monitoring self is possible but no monitor is actually created
4159+
if (UNLIKELY(local_process_id == ctx->process_id)) {
4160+
if (UNLIKELY(memory_ensure_free_opt(ctx, REF_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
4161+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
4162+
}
4163+
uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global);
4164+
term ref = term_from_ref_ticks(ref_ticks, &ctx->heap);
4165+
return ref;
41494166
}
4150-
uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global);
4151-
term ref = term_from_ref_ticks(ref_ticks, &ctx->heap);
4152-
return ref;
4167+
4168+
target = globalcontext_get_process_lock(ctx->global, local_process_id);
41534169
}
41544170

4155-
Context *target = globalcontext_get_process_lock(ctx->global, local_process_id);
41564171
if (IS_NULL_PTR(target)) {
4157-
int res_size = REF_SIZE + TUPLE_SIZE(5);
4172+
int res_size = REF_SIZE + TUPLE_SIZE(5) + target_proc_size;
41584173
if (UNLIKELY(memory_ensure_free_opt(ctx, res_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
41594174
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
41604175
}
@@ -4164,7 +4179,14 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[])
41644179
term_put_tuple_element(down_message_tuple, 0, DOWN_ATOM);
41654180
term_put_tuple_element(down_message_tuple, 1, ref);
41664181
term_put_tuple_element(down_message_tuple, 2, object_type);
4167-
term_put_tuple_element(down_message_tuple, 3, target_pid);
4182+
if (term_is_atom(target_proc)) {
4183+
term target_proc_tuple = term_alloc_tuple(2, &ctx->heap);
4184+
term_put_tuple_element(target_proc_tuple, 0, target_proc);
4185+
term_put_tuple_element(target_proc_tuple, 1, ctx->global->node_name);
4186+
term_put_tuple_element(down_message_tuple, 3, target_proc_tuple);
4187+
} else {
4188+
term_put_tuple_element(down_message_tuple, 3, target_proc);
4189+
}
41684190
term_put_tuple_element(down_message_tuple, 4, NOPROC_ATOM);
41694191
mailbox_send(ctx, down_message_tuple);
41704192
return ref;
@@ -4175,7 +4197,12 @@ static term nif_erlang_monitor(Context *ctx, int argc, term argv[])
41754197
}
41764198
uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global);
41774199
term monitoring_pid = term_from_local_process_id(ctx->process_id);
4178-
struct Monitor *self_monitor = monitor_new(target_pid, ref_ticks, true);
4200+
struct Monitor *self_monitor;
4201+
if (term_is_atom(target_proc)) {
4202+
self_monitor = monitor_registeredname_monitor_new(local_process_id, target_proc, ref_ticks);
4203+
} else {
4204+
self_monitor = monitor_new(target_pid, ref_ticks, true);
4205+
}
41794206
if (IS_NULL_PTR(self_monitor)) {
41804207
globalcontext_get_process_unlock(ctx->global, target);
41814208
RAISE_ERROR(OUT_OF_MEMORY_ATOM);

0 commit comments

Comments
 (0)