Skip to content

Commit ecdc71b

Browse files
committed
Implement monitoring process by registered name
Also simplify gen_server implementation when ServerRef is an atom Also fix CodeQL queries to work with "term _Atomic" Signed-off-by: Paul Guyot <[email protected]>
1 parent 630c11d commit ecdc71b

File tree

9 files changed

+236
-61
lines changed

9 files changed

+236
-61
lines changed

code-queries/non-term-to-term-func.ql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ predicate isTermType(Type t) {
2020
(
2121
t instanceof TypedefType
2222
and isTermType(t.(TypedefType).getBaseType())
23+
) or
24+
(
25+
t instanceof SpecifiedType
26+
and isTermType(t.(SpecifiedType).getBaseType())
2327
)
2428
}
2529

code-queries/term-to-non-term-func.ql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ predicate isTermType(Type t) {
2020
(
2121
t instanceof TypedefType
2222
and isTermType(t.(TypedefType).getBaseType())
23+
) or
24+
(
25+
t instanceof SpecifiedType
26+
and isTermType(t.(SpecifiedType).getBaseType())
2327
)
2428
}
2529

libs/estdlib/src/erlang.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,8 +1133,8 @@ send(_Target, _Message) ->
11331133
%% @end
11341134
%%-----------------------------------------------------------------------------
11351135
-spec monitor
1136-
(Type :: process, Pid :: pid()) -> reference();
1137-
(Type :: port, Port :: port()) -> reference().
1136+
(Type :: process, Pid :: pid() | atom()) -> reference();
1137+
(Type :: port, Port :: port() | atom()) -> reference().
11381138
monitor(_Type, _PidOrPort) ->
11391139
erlang:nif_error(undefined).
11401140

libs/estdlib/src/gen_server.erl

Lines changed: 31 additions & 38 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),
399-
Pid ! {'$stop', Reason},
390+
stop(ServerRef, Reason, Timeout) ->
391+
MonitorRef = monitor(process, ServerRef),
392+
ServerRef ! {'$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,30 @@ 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+
ActualTimeout =
430+
try
431+
ServerRef ! {'$gen_call', {self(), MonitorRef}, Request},
432+
TimeoutMs
433+
catch
434+
error:badarg ->
435+
% Process no longer exists, monitor will send a message
436+
1000
437+
end,
444438
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} ->
439+
{'DOWN', MonitorRef, process, _, {E, []} = _Reason} ->
440+
erlang:exit({E, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}});
441+
{'DOWN', MonitorRef, process, _, {_E, _L} = Reason} ->
448442
erlang:exit(Reason);
449-
{'DOWN', MonitorRef, process, Pid, Atom} when is_atom(Atom) ->
450-
erlang:exit({Atom, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}});
443+
{'DOWN', MonitorRef, process, _, Atom} when is_atom(Atom) ->
444+
erlang:exit({Atom, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}});
451445
{MonitorRef, Reply} ->
452446
demonitor(MonitorRef, [flush]),
453447
Reply
454-
after TimeoutMs ->
448+
after ActualTimeout ->
455449
demonitor(MonitorRef, [flush]),
456-
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}})
450+
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}})
457451
end.
458452

459453
%%-----------------------------------------------------------------------------
@@ -467,15 +461,14 @@ call(Pid, Request, TimeoutMs) when is_pid(Pid) ->
467461
%% @end
468462
%%-----------------------------------------------------------------------------
469463
-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},
464+
cast(ServerRef, Request) ->
465+
try
466+
ServerRef ! {'$gen_cast', Request}
467+
catch
468+
error:badarg ->
469+
% Process does not exist, ignore error
470+
ok
471+
end,
479472
ok.
480473

481474
%%-----------------------------------------------------------------------------

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

0 commit comments

Comments
 (0)