Skip to content

Commit bc50bc1

Browse files
committed
Forward port changes from v0.6 release branch
Merge #1635 "implement link protocol with unlink id and acknowledgement" from release-0.6.
2 parents 8971c26 + 5eb7cf8 commit bc50bc1

File tree

15 files changed

+671
-208
lines changed

15 files changed

+671
-208
lines changed

CHANGELOG.md

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

106107
### Changed
107108

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
@@ -698,27 +698,3 @@ Module *globalcontext_get_module_by_index(GlobalContext *global, int index)
698698
SMP_RWLOCK_UNLOCK(global->modules_lock);
699699
return result;
700700
}
701-
702-
bool globalcontext_demonitor(GlobalContext *global, uint64_t ref_ticks)
703-
{
704-
struct ListHead *pitem;
705-
706-
struct ListHead *processes_table_list = synclist_wrlock(&global->processes_table);
707-
LIST_FOR_EACH (pitem, processes_table_list) {
708-
Context *p = GET_LIST_ENTRY(pitem, Context, processes_table_head);
709-
710-
struct ListHead *item;
711-
LIST_FOR_EACH (item, &p->monitors_head) {
712-
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
713-
if (monitor->ref_ticks == ref_ticks) {
714-
list_remove(&monitor->monitor_list_head);
715-
free(monitor);
716-
synclist_unlock(&global->processes_table);
717-
return true;
718-
}
719-
}
720-
}
721-
722-
synclist_unlock(&global->processes_table);
723-
return false;
724-
}

src/libAtomVM/globalcontext.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -491,17 +491,6 @@ Module *globalcontext_get_module(GlobalContext *global, AtomString module_name_a
491491
*/
492492
Module *globalcontext_load_module_from_avm(GlobalContext *global, const char *module_name);
493493

494-
/**
495-
* @brief remove a monitor
496-
*
497-
* @details iterate on the list of all processes and then on each monitor
498-
* to find a given monitor, and remove it
499-
* @param global the global context
500-
* @param ref_ticks the reference to the monitor
501-
* @return true if the monitor was found
502-
*/
503-
bool globalcontext_demonitor(GlobalContext *global, uint64_t ref_ticks);
504-
505494
#ifndef __cplusplus
506495
static inline uint64_t globalcontext_get_ref_ticks(GlobalContext *global)
507496
{

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
@@ -3879,19 +3879,22 @@ static term nif_erlang_link(Context *ctx, int argc, term argv[])
38793879
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
38803880
}
38813881

3882-
term callee_pid = term_from_local_process_id(ctx->process_id);
3883-
struct Monitor *other_link = monitor_link_new(callee_pid);
3882+
term self_pid = term_from_local_process_id(ctx->process_id);
3883+
struct Monitor *other_link = monitor_link_new(self_pid);
38843884
if (IS_NULL_PTR(other_link)) {
38853885
globalcontext_get_process_unlock(ctx->global, target);
3886-
free(self_link);
38873886
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
38883887
}
38893888

3889+
if (UNLIKELY(!context_add_monitor(ctx, self_link))) {
3890+
globalcontext_get_process_unlock(ctx->global, target);
3891+
free(other_link);
3892+
return TRUE_ATOM;
3893+
}
3894+
38903895
mailbox_send_monitor_signal(target, MonitorSignal, other_link);
38913896
globalcontext_get_process_unlock(ctx->global, target);
38923897

3893-
context_add_monitor(ctx, self_link);
3894-
38953898
return TRUE_ATOM;
38963899
}
38973900

@@ -3909,9 +3912,14 @@ static term nif_erlang_unlink(Context *ctx, int argc, term argv[])
39093912
return TRUE_ATOM;
39103913
}
39113914

3912-
context_unlink(ctx, term_from_local_process_id(target->process_id));
3913-
term callee_pid = term_from_local_process_id(ctx->process_id);
3914-
mailbox_send_immediate_signal(target, UnlinkSignal, callee_pid);
3915+
uint64_t unlink_id;
3916+
if (UNLIKELY(!context_set_unlink_id(ctx, argv[0], &unlink_id))) {
3917+
globalcontext_get_process_unlock(ctx->global, target);
3918+
return TRUE_ATOM;
3919+
}
3920+
3921+
term self_pid = term_from_local_process_id(ctx->process_id);
3922+
mailbox_send_immediate_ref_signal(target, UnlinkIDSignal, self_pid, unlink_id);
39153923
globalcontext_get_process_unlock(ctx->global, target);
39163924

39173925
return TRUE_ATOM;

0 commit comments

Comments
 (0)