Skip to content

Commit 5eb7cf8

Browse files
committed
Merge pull request #1635 from pguyot/w16/fix-unlink-protocol
Implement link protocol with unlink id and acknowledgement 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 54cf115 + 9f299e7 commit 5eb7cf8

File tree

13 files changed

+657
-200
lines changed

13 files changed

+657
-200
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ memory error
6666
- Fixed segfault when calling `lists:reverse/1` (#1600)
6767
- Fixed nif_atomvm_posix_read GC bug
6868
- Fixed `erlang:is_number/1` function, now returns true also for floats
69+
- Fixed unlink protocol and add support for `link/1` on ports
6970

7071
### Changed
7172

src/libAtomVM/context.c

Lines changed: 327 additions & 102 deletions
Large diffs are not rendered by default.

src/libAtomVM/context.h

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -149,18 +149,45 @@ struct Context
149149
typedef struct Context Context;
150150
#endif
151151

152-
#define CONTEXT_MONITOR_RESOURCE_TAG 0x2
153-
#define CONTEXT_MONITOR_MONITORED_PID_TAG 0x3
154-
#define CONTEXT_MONITOR_MONITORING_PID_TAG 0x1
152+
enum ContextMonitorType
153+
{
154+
CONTEXT_MONITOR_LINK_LOCAL,
155+
CONTEXT_MONITOR_MONITORING_LOCAL,
156+
CONTEXT_MONITOR_MONITORED_LOCAL,
157+
CONTEXT_MONITOR_RESOURCE,
158+
};
159+
160+
#define UNLINK_ID_LINK_ACTIVE 0x0
155161

156162
/**
157163
* @brief A regular monitor or a half link.
158164
*/
159165
struct Monitor
160166
{
161167
struct ListHead monitor_list_head;
162-
uint64_t ref_ticks; // 0 for links
163-
term monitor_obj; // pid for links, CONTEXT_MONITOR_*_TAG for monitors
168+
enum ContextMonitorType monitor_type;
169+
};
170+
171+
struct LinkLocalMonitor
172+
{
173+
struct Monitor monitor;
174+
uint64_t unlink_id;
175+
term link_local_process_id;
176+
};
177+
178+
struct MonitorLocalMonitor
179+
{
180+
struct Monitor monitor;
181+
uint64_t ref_ticks;
182+
term monitor_obj;
183+
};
184+
185+
// The other half is called ResourceMonitor and is a linked list of resources
186+
struct ResourceContextMonitor
187+
{
188+
struct Monitor monitor;
189+
uint64_t ref_ticks;
190+
void *resource_obj;
164191
};
165192

166193
struct ExtendedRegister
@@ -363,9 +390,10 @@ void context_process_kill_signal(Context *ctx, struct TermSignal *signal);
363390
* @brief Process a process info request signal.
364391
*
365392
* @param ctx the context being executed
366-
* @param signal the kill message
393+
* @param signal the process info signal
394+
* @param process_table_locked whether process table is already locked
367395
*/
368-
void context_process_process_info_request_signal(Context *ctx, struct BuiltInAtomRequestSignal *signal);
396+
void context_process_process_info_request_signal(Context *ctx, struct BuiltInAtomRequestSignal *signal, bool process_table_locked);
369397

370398
/**
371399
* @brief Process a trap answer signal.
@@ -385,6 +413,23 @@ bool context_process_signal_trap_answer(Context *ctx, struct TermSignal *signal)
385413
*/
386414
void context_process_flush_monitor_signal(Context *ctx, uint64_t ref_ticks, bool info);
387415

416+
/**
417+
* @brief Process a link exit signal.
418+
*
419+
* @param ctx the context being executed
420+
* @param signal the signal with the exit info tuple
421+
* @return true if the process is trapping exit and info tuple was enqueued as a message;
422+
*/
423+
bool context_process_link_exit_signal(Context *ctx, struct TermSignal *signal);
424+
425+
/**
426+
* @brief Process a monitor down signal.
427+
*
428+
* @param ctx the context being executed
429+
* @param signal the signal with the down info tuple
430+
*/
431+
void context_process_monitor_down_signal(Context *ctx, struct TermSignal *signal);
432+
388433
/**
389434
* @brief Get process information.
390435
*
@@ -421,30 +466,53 @@ struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monito
421466
*
422467
* @param resource resource object
423468
* @param ref_ticks reference associated with the monitor
424-
* @param process_id process being monitored
425469
* @return the allocated resource monitor or NULL if allocation failed
426470
*/
427471
struct Monitor *monitor_resource_monitor_new(void *resource, uint64_t ref_ticks);
428472

429473
/**
430474
* @brief Half-unlink process to another process
431-
* @details Called within the process only. For the other end of the
432-
* link, an UnlinkSignal is sent that calls this function.
475+
* @details If process is found, an unlink id is generated and the link is
476+
* deactivated.
433477
*
434478
* @param ctx the context being executed
435-
* @param monitor_pid process to unlink from
436-
* @return 0 on success
479+
* @param link_pid process to unlink from
480+
* @param unlink_id on output, unlink id to send to the target process
481+
* @return true if process was found
437482
*/
438-
void context_unlink(Context *ctx, term monitor_pid);
483+
bool context_set_unlink_id(Context *ctx, term link_pid, uint64_t *unlink_id);
439484

440485
/**
441-
* @brief Destroy a monitor on a process.
442-
* @details Called within the process only. This function is called from
443-
* DemonitorSignal.
486+
* @brief Half-unlink process to another process
487+
* @details Called within the process only when an UnlinkID signal is received.
488+
* If link is found, remove it and sends an UnlinkIDAck signal to the linked
489+
* process.
490+
*
491+
* @param ctx the context being executed
492+
* @param link_pid process to unlink from
493+
* @param unlink_id unlink id from the signal
494+
* @param process_table_locked whether process table is already locked
495+
*/
496+
void context_ack_unlink(Context *ctx, term link_pid, uint64_t unlink_id, bool process_table_locked);
497+
498+
/**
499+
* @brief Half-unlink process to another process
500+
* @details Called within the process only when an UnlinkIDAck signal is received.
501+
* If link is found and matches, remove it.
444502
*
445503
* @param ctx the context being executed
504+
* @param link_pid process to unlink from
505+
* @param unlink_id unlink id from the signal
506+
*/
507+
void context_unlink_ack(Context *ctx, term link_pid, uint64_t unlink_id);
508+
509+
/**
510+
* @brief Destroy a monitor on a process (monitoring, monitored or resource)
511+
* @details Called within the process only. This function is called from
512+
* DemonitorSignal as well as demonitor nif on monitoring process.
513+
*
514+
* @param ctx the context being executed (monitoring or monitored)
446515
* @param ref_ticks reference of the monitor to remove
447-
* @return 0 on success
448516
*/
449517
void context_demonitor(Context *ctx, uint64_t ref_ticks);
450518

@@ -467,9 +535,12 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori
467535
* only exist once.
468536
*
469537
* @param ctx the context being executed
470-
* @param new_monitor monitor object to add
538+
* @param new_monitor monitor object to add (ownership belongs to context
539+
* afterwards)
540+
* @return true if the monitor was added, false if it already existed and
541+
* new_monitor waw freed.
471542
*/
472-
void context_add_monitor(Context *ctx, struct Monitor *new_monitor);
543+
bool context_add_monitor(Context *ctx, struct Monitor *new_monitor);
473544

474545
#ifdef __cplusplus
475546
}

src/libAtomVM/globalcontext.c

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -646,27 +646,3 @@ Module *globalcontext_get_module_by_index(GlobalContext *global, int index)
646646
SMP_RWLOCK_UNLOCK(global->modules_lock);
647647
return result;
648648
}
649-
650-
bool globalcontext_demonitor(GlobalContext *global, uint64_t ref_ticks)
651-
{
652-
struct ListHead *pitem;
653-
654-
struct ListHead *processes_table_list = synclist_wrlock(&global->processes_table);
655-
LIST_FOR_EACH (pitem, processes_table_list) {
656-
Context *p = GET_LIST_ENTRY(pitem, Context, processes_table_head);
657-
658-
struct ListHead *item;
659-
LIST_FOR_EACH (item, &p->monitors_head) {
660-
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
661-
if (monitor->ref_ticks == ref_ticks) {
662-
list_remove(&monitor->monitor_list_head);
663-
free(monitor);
664-
synclist_unlock(&global->processes_table);
665-
return true;
666-
}
667-
}
668-
}
669-
670-
synclist_unlock(&global->processes_table);
671-
return false;
672-
}

src/libAtomVM/globalcontext.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -463,17 +463,6 @@ Module *globalcontext_get_module_by_index(GlobalContext *global, int index);
463463
*/
464464
Module *globalcontext_get_module(GlobalContext *global, AtomString module_name_atom);
465465

466-
/**
467-
* @brief remove a monitor
468-
*
469-
* @details iterate on the list of all processes and then on each monitor
470-
* to find a given monitor, and remove it
471-
* @param global the global context
472-
* @param ref_ticks the reference to the monitor
473-
* @return true if the monitor was found
474-
*/
475-
bool globalcontext_demonitor(GlobalContext *global, uint64_t ref_ticks);
476-
477466
#ifndef __cplusplus
478467
static inline uint64_t globalcontext_get_ref_ticks(GlobalContext *global)
479468
{

src/libAtomVM/mailbox.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ void mailbox_message_dispose(MailboxMessage *m, Heap *heap)
9696
break;
9797
}
9898
case KillSignal:
99-
case TrapAnswerSignal: {
99+
case TrapAnswerSignal:
100+
case LinkExitSignal:
101+
case MonitorDownSignal: {
100102
struct TermSignal *term_signal = CONTAINER_OF(m, struct TermSignal, base);
101103
term mso_list = term_signal->storage[STORAGE_MSO_LIST_INDEX];
102104
HeapFragment *fragment = mailbox_message_to_heap_fragment(term_signal, term_signal->heap_end);
@@ -109,12 +111,17 @@ void mailbox_message_dispose(MailboxMessage *m, Heap *heap)
109111
free(request_signal);
110112
break;
111113
}
112-
case TrapExceptionSignal:
113-
case UnlinkSignal: {
114+
case TrapExceptionSignal: {
114115
struct ImmediateSignal *immediate_signal = CONTAINER_OF(m, struct ImmediateSignal, base);
115116
free(immediate_signal);
116117
break;
117118
}
119+
case UnlinkIDSignal:
120+
case UnlinkIDAckSignal: {
121+
struct ImmediateRefSignal *immediate_ref_signal = CONTAINER_OF(m, struct ImmediateRefSignal, base);
122+
free(immediate_ref_signal);
123+
break;
124+
}
118125
case FlushMonitorSignal:
119126
case FlushInfoMonitorSignal:
120127
case DemonitorSignal: {
@@ -291,6 +298,20 @@ void mailbox_send_ref_signal(Context *c, enum MessageType type, uint64_t ref_tic
291298
mailbox_post_message(c, &ref_signal->base);
292299
}
293300

301+
void mailbox_send_immediate_ref_signal(Context *c, enum MessageType type, term immediate, uint64_t ref_ticks)
302+
{
303+
struct ImmediateRefSignal *immediate_ref_signal = malloc(sizeof(struct ImmediateRefSignal));
304+
if (IS_NULL_PTR(immediate_ref_signal)) {
305+
fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__);
306+
return;
307+
}
308+
immediate_ref_signal->base.type = type;
309+
immediate_ref_signal->immediate = immediate;
310+
immediate_ref_signal->ref_ticks = ref_ticks;
311+
312+
mailbox_post_message(c, &immediate_ref_signal->base);
313+
}
314+
294315
void mailbox_send_monitor_signal(Context *c, enum MessageType type, struct Monitor *monitor)
295316
{
296317
struct MonitorPointerSignal *monitor_signal = malloc(sizeof(struct MonitorPointerSignal));

src/libAtomVM/mailbox.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@ enum MessageType
8282
FlushMonitorSignal,
8383
FlushInfoMonitorSignal,
8484
MonitorSignal,
85-
UnlinkSignal,
85+
UnlinkIDSignal,
86+
UnlinkIDAckSignal,
87+
LinkExitSignal,
8688
DemonitorSignal,
89+
MonitorDownSignal,
8790
};
8891

8992
struct MailboxMessage
@@ -137,6 +140,14 @@ struct RefSignal
137140
uint64_t ref_ticks;
138141
};
139142

143+
struct ImmediateRefSignal
144+
{
145+
MailboxMessage base;
146+
147+
term immediate;
148+
uint64_t ref_ticks;
149+
};
150+
140151
struct MonitorPointerSignal
141152
{
142153
MailboxMessage base;
@@ -240,6 +251,16 @@ void mailbox_send_built_in_atom_request_signal(
240251
*/
241252
void mailbox_send_ref_signal(Context *c, enum MessageType type, uint64_t ref_ticks);
242253

254+
/**
255+
* @brief Sends an immediate and ref signal to a certain mailbox.
256+
*
257+
* @param c the process context.
258+
* @param type the type of the signal
259+
* @param immediate the immediate term to send (atom or pid)
260+
* @param ref_ticks the ref
261+
*/
262+
void mailbox_send_immediate_ref_signal(Context *c, enum MessageType type, term immediate, uint64_t ref_ticks);
263+
243264
/**
244265
* @brief Sends a ref immediate signal to a certain mailbox.
245266
*

src/libAtomVM/nifs.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3656,19 +3656,22 @@ static term nif_erlang_link(Context *ctx, int argc, term argv[])
36563656
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
36573657
}
36583658

3659-
term callee_pid = term_from_local_process_id(ctx->process_id);
3660-
struct Monitor *other_link = monitor_link_new(callee_pid);
3659+
term self_pid = term_from_local_process_id(ctx->process_id);
3660+
struct Monitor *other_link = monitor_link_new(self_pid);
36613661
if (IS_NULL_PTR(other_link)) {
36623662
globalcontext_get_process_unlock(ctx->global, target);
3663-
free(self_link);
36643663
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
36653664
}
36663665

3666+
if (UNLIKELY(!context_add_monitor(ctx, self_link))) {
3667+
globalcontext_get_process_unlock(ctx->global, target);
3668+
free(other_link);
3669+
return TRUE_ATOM;
3670+
}
3671+
36673672
mailbox_send_monitor_signal(target, MonitorSignal, other_link);
36683673
globalcontext_get_process_unlock(ctx->global, target);
36693674

3670-
context_add_monitor(ctx, self_link);
3671-
36723675
return TRUE_ATOM;
36733676
}
36743677

@@ -3686,9 +3689,14 @@ static term nif_erlang_unlink(Context *ctx, int argc, term argv[])
36863689
return TRUE_ATOM;
36873690
}
36883691

3689-
context_unlink(ctx, term_from_local_process_id(target->process_id));
3690-
term callee_pid = term_from_local_process_id(ctx->process_id);
3691-
mailbox_send_immediate_signal(target, UnlinkSignal, callee_pid);
3692+
uint64_t unlink_id;
3693+
if (UNLIKELY(!context_set_unlink_id(ctx, argv[0], &unlink_id))) {
3694+
globalcontext_get_process_unlock(ctx->global, target);
3695+
return TRUE_ATOM;
3696+
}
3697+
3698+
term self_pid = term_from_local_process_id(ctx->process_id);
3699+
mailbox_send_immediate_ref_signal(target, UnlinkIDSignal, self_pid, unlink_id);
36923700
globalcontext_get_process_unlock(ctx->global, target);
36933701

36943702
return TRUE_ATOM;

0 commit comments

Comments
 (0)