Skip to content

Commit 390c50b

Browse files
committed
Merge pull request #1792 from pguyot/w32/monitor-registered-process
Implement monitoring process by registered name These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents ba980b2 + ba46b84 commit 390c50b

File tree

9 files changed

+237
-60
lines changed

9 files changed

+237
-60
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
@@ -1138,8 +1138,8 @@ send(_Target, _Message) ->
11381138
%% @end
11391139
%%-----------------------------------------------------------------------------
11401140
-spec monitor
1141-
(Type :: process, Pid :: pid()) -> reference();
1142-
(Type :: port, Port :: port()) -> reference().
1141+
(Type :: process, Pid :: pid() | atom()) -> reference();
1142+
(Type :: port, Port :: port() | atom()) -> reference().
11431143
monitor(_Type, _PidOrPort) ->
11441144
erlang:nif_error(undefined).
11451145

libs/estdlib/src/gen_server.erl

Lines changed: 32 additions & 37 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,32 @@ 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+
ok =
430+
try
431+
ServerRef ! {'$gen_call', {self(), MonitorRef}, Request},
432+
ok
433+
catch
434+
error:badarg ->
435+
% Process no longer exists, monitor will send a message
436+
ok
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
454448
after TimeoutMs ->
449+
% If TimeoutMS is small enough (0), the error message might be timeout
450+
% instead of noproc as there could be a race condition with the monitor.
455451
demonitor(MonitorRef, [flush]),
456-
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}})
452+
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [ServerRef, Request]}})
457453
end.
458454

459455
%%-----------------------------------------------------------------------------
@@ -467,15 +463,14 @@ call(Pid, Request, TimeoutMs) when is_pid(Pid) ->
467463
%% @end
468464
%%-----------------------------------------------------------------------------
469465
-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},
466+
cast(ServerRef, Request) ->
467+
try
468+
ServerRef ! {'$gen_cast', Request}
469+
catch
470+
error:badarg ->
471+
% Process does not exist, ignore error
472+
ok
473+
end,
479474
ok.
480475

481476
%%-----------------------------------------------------------------------------

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.
@@ -666,6 +687,18 @@ static struct Monitor *context_monitors_handle_terminate(Context *ctx)
666687
free(monitor);
667688
break;
668689
}
690+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
691+
// We are the monitoring process.
692+
struct MonitorLocalRegisteredNameMonitor *monitoring_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
693+
int32_t local_process_id = monitoring_monitor->monitor_process_id;
694+
Context *target = globalcontext_get_process_nolock(glb, local_process_id);
695+
if (LIKELY(target != NULL)) {
696+
// target can be null if we didn't process a MonitorDownSignal
697+
mailbox_send_ref_signal(target, DemonitorSignal, monitoring_monitor->ref_ticks);
698+
}
699+
free(monitor);
700+
break;
701+
}
669702
case CONTEXT_MONITOR_LINK_LOCAL: {
670703
struct LinkLocalMonitor *link_monitor = CONTAINER_OF(monitor, struct LinkLocalMonitor, monitor);
671704
// Handle the case of inactive link.
@@ -790,6 +823,20 @@ struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monito
790823
return &monitor->monitor;
791824
}
792825

826+
struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, uint64_t ref_ticks)
827+
{
828+
struct MonitorLocalRegisteredNameMonitor *monitor = malloc(sizeof(struct MonitorLocalRegisteredNameMonitor));
829+
if (IS_NULL_PTR(monitor)) {
830+
return NULL;
831+
}
832+
monitor->monitor.monitor_type = CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME;
833+
monitor->monitor_process_id = monitor_process_id;
834+
monitor->monitor_name = monitor_name;
835+
monitor->ref_ticks = ref_ticks;
836+
837+
return &monitor->monitor;
838+
}
839+
793840
struct Monitor *monitor_resource_monitor_new(void *resource, uint64_t ref_ticks)
794841
{
795842
struct ResourceContextMonitor *monitor = malloc(sizeof(struct ResourceContextMonitor));
@@ -829,6 +876,17 @@ bool context_add_monitor(Context *ctx, struct Monitor *new_monitor)
829876
}
830877
break;
831878
}
879+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
880+
struct MonitorLocalRegisteredNameMonitor *new_local_registeredname_monitor = CONTAINER_OF(new_monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
881+
struct MonitorLocalRegisteredNameMonitor *existing_local_registeredname_monitor = CONTAINER_OF(existing, struct MonitorLocalRegisteredNameMonitor, monitor);
882+
if (UNLIKELY(existing_local_registeredname_monitor->monitor_process_id == new_local_registeredname_monitor->monitor_process_id
883+
&& existing_local_registeredname_monitor->monitor_name == new_local_registeredname_monitor->monitor_name
884+
&& existing_local_registeredname_monitor->ref_ticks == new_local_registeredname_monitor->ref_ticks)) {
885+
free(new_monitor);
886+
return false;
887+
}
888+
break;
889+
}
832890
case CONTEXT_MONITOR_RESOURCE: {
833891
struct ResourceContextMonitor *new_resource_monitor = CONTAINER_OF(new_monitor, struct ResourceContextMonitor, monitor);
834892
struct ResourceContextMonitor *existing_resource_monitor = CONTAINER_OF(existing, struct ResourceContextMonitor, monitor);
@@ -976,6 +1034,15 @@ void context_demonitor(Context *ctx, uint64_t ref_ticks)
9761034
}
9771035
break;
9781036
}
1037+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
1038+
struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
1039+
if (local_registeredname_monitor->ref_ticks == ref_ticks) {
1040+
list_remove(&monitor->monitor_list_head);
1041+
free(monitor);
1042+
return;
1043+
}
1044+
break;
1045+
}
9791046
case CONTEXT_MONITOR_RESOURCE: {
9801047
struct ResourceContextMonitor *resource_monitor = CONTAINER_OF(monitor, struct ResourceContextMonitor, monitor);
9811048
if (resource_monitor->ref_ticks == ref_ticks) {
@@ -1006,6 +1073,14 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori
10061073
}
10071074
break;
10081075
}
1076+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
1077+
struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
1078+
if (local_registeredname_monitor->ref_ticks == ref_ticks) {
1079+
*is_monitoring = true;
1080+
return term_from_local_process_id(local_registeredname_monitor->monitor_process_id);
1081+
}
1082+
break;
1083+
}
10091084
case CONTEXT_MONITOR_LINK_LOCAL:
10101085
case CONTEXT_MONITOR_LINK_REMOTE:
10111086
case CONTEXT_MONITOR_RESOURCE:
@@ -1146,6 +1221,16 @@ COLD_FUNC void context_dump(Context *ctx)
11461221
fprintf(stderr, "\n");
11471222
break;
11481223
}
1224+
case CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME: {
1225+
struct MonitorLocalRegisteredNameMonitor *local_registeredname_monitor = CONTAINER_OF(monitor, struct MonitorLocalRegisteredNameMonitor, monitor);
1226+
fprintf(stderr, "monitor to ");
1227+
term_display(stderr, local_registeredname_monitor->monitor_name, ctx);
1228+
fprintf(stderr, " (");
1229+
term_display(stderr, term_from_local_process_id(local_registeredname_monitor->monitor_process_id), ctx);
1230+
fprintf(stderr, ") ref=%lu", (long unsigned) local_registeredname_monitor->ref_ticks);
1231+
fprintf(stderr, "\n");
1232+
break;
1233+
}
11491234
case CONTEXT_MONITOR_RESOURCE: {
11501235
struct ResourceContextMonitor *resource_monitor = CONTAINER_OF(monitor, struct ResourceContextMonitor, monitor);
11511236
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)