Skip to content

Commit ad26b6f

Browse files
committed
Merge pull request #1637 from pguyot/w17/merge-main
Merge main into feature/distributed-erlang 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 79f9533 + 6523d1b commit ad26b6f

25 files changed

+858
-246
lines changed

.github/workflows/build-and-test-other.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
9595
- arch: "arm32v7"
9696
platform: "arm/v7"
97-
tag: "bullseye"
97+
tag: "bookworm"
9898
# -D_FILE_OFFSET_BITS=64 is required for making atomvm:posix_readdir/1 test work
9999
# otherwise readdir will fail due to 64 bits inode numbers with 32 bit ino_t
100100
cflags: "-mcpu=cortex-a7 -mfloat-abi=hard -O2 -mthumb -mthumb-interwork -D_FILE_OFFSET_BITS=64"

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5656
- Fixed `gen_server` internal messages to match OTP so it works across erlang distribution
5757
- Utilize reserved `phy_init` partition on ESP32 to store wifi calibration for faster connections.
5858
- Support for zero count in `lists:duplicate/2`.
59+
- packbeam: fix memory leak preventing building with address sanitizer
5960

6061
## [0.6.6] - Unreleased
6162

@@ -119,6 +120,7 @@ memory error
119120
- Fixed segfault when calling `lists:reverse/1` (#1600)
120121
- Fixed nif_atomvm_posix_read GC bug
121122
- Fixed `erlang:is_number/1` function, now returns true also for floats
123+
- Fixed unlink protocol and add support for `link/1` on ports
122124

123125
### Changed
124126

CMakeModules/BuildElixir.cmake

+61
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,64 @@ macro(pack_runnable avm_name main)
9494
add_dependencies(${avm_name} ${avm_name}_main ${ARCHIVE_TARGETS} PackBEAM)
9595

9696
endmacro()
97+
98+
99+
macro(pack_test avm_name main)
100+
find_package(Elixir REQUIRED)
101+
102+
# Compile the main module
103+
add_custom_command(
104+
OUTPUT Elixir.${main}.beam
105+
COMMAND elixirc ${CMAKE_CURRENT_SOURCE_DIR}/${main}.ex
106+
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${main}.ex
107+
COMMENT "Compiling ${main}.ex"
108+
VERBATIM
109+
)
110+
111+
add_custom_target(
112+
${avm_name}_main
113+
DEPENDS Elixir.${main}.beam
114+
)
115+
116+
# Compile test modules
117+
foreach(module_name ${ARGN})
118+
add_custom_command(
119+
OUTPUT Elixir.${module_name}.beam
120+
COMMAND elixirc ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.ex
121+
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${module_name}.ex
122+
COMMENT "Compiling ${module_name}.ex"
123+
VERBATIM
124+
)
125+
set(TEST_BEAMS ${TEST_BEAMS} Elixir.${module_name}.beam)
126+
endforeach()
127+
128+
add_custom_target(
129+
${avm_name}_tests
130+
DEPENDS ${TEST_BEAMS}
131+
)
132+
133+
if(AVM_RELEASE)
134+
set(INCLUDE_LINES "")
135+
else()
136+
set(INCLUDE_LINES "-i")
137+
endif()
138+
139+
# Set up standard libraries
140+
set(ARCHIVE_TARGETS estdlib eavmlib exavmlib etest)
141+
foreach(archive_name ${ARCHIVE_TARGETS})
142+
if(${archive_name} STREQUAL "exavmlib")
143+
set(ARCHIVES ${ARCHIVES} ${CMAKE_BINARY_DIR}/libs/${archive_name}/lib/${archive_name}.avm)
144+
else()
145+
set(ARCHIVES ${ARCHIVES} ${CMAKE_BINARY_DIR}/libs/${archive_name}/src/${archive_name}.avm)
146+
endif()
147+
endforeach()
148+
149+
add_custom_target(
150+
${avm_name} ALL
151+
COMMAND ${CMAKE_BINARY_DIR}/tools/packbeam/PackBEAM ${INCLUDE_LINES} ${avm_name}.avm Elixir.${main}.beam ${TEST_BEAMS} ${ARCHIVES}
152+
COMMENT "Packing test ${avm_name}.avm"
153+
VERBATIM
154+
)
155+
add_dependencies(${avm_name} ${avm_name}_main ${avm_name}_tests ${ARCHIVE_TARGETS} PackBEAM)
156+
157+
endmacro()

src/libAtomVM/context.c

+342-102
Large diffs are not rendered by default.

src/libAtomVM/context.h

+90-19
Original file line numberDiff line numberDiff line change
@@ -150,18 +150,45 @@ struct Context
150150
typedef struct Context Context;
151151
#endif
152152

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

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

167194
struct ExtendedRegister
@@ -364,9 +391,10 @@ void context_process_kill_signal(Context *ctx, struct TermSignal *signal);
364391
* @brief Process a process info request signal.
365392
*
366393
* @param ctx the context being executed
367-
* @param signal the kill message
394+
* @param signal the process info signal
395+
* @param process_table_locked whether process table is already locked
368396
*/
369-
void context_process_process_info_request_signal(Context *ctx, struct BuiltInAtomRequestSignal *signal);
397+
void context_process_process_info_request_signal(Context *ctx, struct BuiltInAtomRequestSignal *signal, bool process_table_locked);
370398

371399
/**
372400
* @brief Process a trap answer signal.
@@ -395,6 +423,23 @@ void context_process_flush_monitor_signal(Context *ctx, uint64_t ref_ticks, bool
395423
*/
396424
bool context_process_signal_set_group_leader(Context *ctx, struct TermSignal *signal);
397425

426+
/**
427+
* @brief Process a link exit signal.
428+
*
429+
* @param ctx the context being executed
430+
* @param signal the signal with the exit info tuple
431+
* @return true if the process is trapping exit and info tuple was enqueued as a message;
432+
*/
433+
bool context_process_link_exit_signal(Context *ctx, struct TermSignal *signal);
434+
435+
/**
436+
* @brief Process a monitor down signal.
437+
*
438+
* @param ctx the context being executed
439+
* @param signal the signal with the down info tuple
440+
*/
441+
void context_process_monitor_down_signal(Context *ctx, struct TermSignal *signal);
442+
398443
/**
399444
* @brief Get process information.
400445
*
@@ -431,30 +476,53 @@ struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monito
431476
*
432477
* @param resource resource object
433478
* @param ref_ticks reference associated with the monitor
434-
* @param process_id process being monitored
435479
* @return the allocated resource monitor or NULL if allocation failed
436480
*/
437481
struct Monitor *monitor_resource_monitor_new(void *resource, uint64_t ref_ticks);
438482

439483
/**
440484
* @brief Half-unlink process to another process
441-
* @details Called within the process only. For the other end of the
442-
* link, an UnlinkSignal is sent that calls this function.
485+
* @details If process is found, an unlink id is generated and the link is
486+
* deactivated.
443487
*
444488
* @param ctx the context being executed
445-
* @param monitor_pid process to unlink from
446-
* @return 0 on success
489+
* @param link_pid process to unlink from
490+
* @param unlink_id on output, unlink id to send to the target process
491+
* @return true if process was found
447492
*/
448-
void context_unlink(Context *ctx, term monitor_pid);
493+
bool context_set_unlink_id(Context *ctx, term link_pid, uint64_t *unlink_id);
449494

450495
/**
451-
* @brief Destroy a monitor on a process.
452-
* @details Called within the process only. This function is called from
453-
* DemonitorSignal.
496+
* @brief Half-unlink process to another process
497+
* @details Called within the process only when an UnlinkID signal is received.
498+
* If link is found, remove it and sends an UnlinkIDAck signal to the linked
499+
* process.
500+
*
501+
* @param ctx the context being executed
502+
* @param link_pid process to unlink from
503+
* @param unlink_id unlink id from the signal
504+
* @param process_table_locked whether process table is already locked
505+
*/
506+
void context_ack_unlink(Context *ctx, term link_pid, uint64_t unlink_id, bool process_table_locked);
507+
508+
/**
509+
* @brief Half-unlink process to another process
510+
* @details Called within the process only when an UnlinkIDAck signal is received.
511+
* If link is found and matches, remove it.
454512
*
455513
* @param ctx the context being executed
514+
* @param link_pid process to unlink from
515+
* @param unlink_id unlink id from the signal
516+
*/
517+
void context_unlink_ack(Context *ctx, term link_pid, uint64_t unlink_id);
518+
519+
/**
520+
* @brief Destroy a monitor on a process (monitoring, monitored or resource)
521+
* @details Called within the process only. This function is called from
522+
* DemonitorSignal as well as demonitor nif on monitoring process.
523+
*
524+
* @param ctx the context being executed (monitoring or monitored)
456525
* @param ref_ticks reference of the monitor to remove
457-
* @return 0 on success
458526
*/
459527
void context_demonitor(Context *ctx, uint64_t ref_ticks);
460528

@@ -477,9 +545,12 @@ term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitori
477545
* only exist once.
478546
*
479547
* @param ctx the context being executed
480-
* @param new_monitor monitor object to add
548+
* @param new_monitor monitor object to add (ownership belongs to context
549+
* afterwards)
550+
* @return true if the monitor was added, false if it already existed and
551+
* new_monitor waw freed.
481552
*/
482-
void context_add_monitor(Context *ctx, struct Monitor *new_monitor);
553+
bool context_add_monitor(Context *ctx, struct Monitor *new_monitor);
483554

484555
#ifdef __cplusplus
485556
}

src/libAtomVM/globalcontext.c

-24
Original file line numberDiff line numberDiff line change
@@ -734,27 +734,3 @@ Module *globalcontext_get_module_by_index(GlobalContext *global, int index)
734734
SMP_RWLOCK_UNLOCK(global->modules_lock);
735735
return result;
736736
}
737-
738-
bool globalcontext_demonitor(GlobalContext *global, uint64_t ref_ticks)
739-
{
740-
struct ListHead *pitem;
741-
742-
struct ListHead *processes_table_list = synclist_wrlock(&global->processes_table);
743-
LIST_FOR_EACH (pitem, processes_table_list) {
744-
Context *p = GET_LIST_ENTRY(pitem, Context, processes_table_head);
745-
746-
struct ListHead *item;
747-
LIST_FOR_EACH (item, &p->monitors_head) {
748-
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
749-
if (monitor->ref_ticks == ref_ticks) {
750-
list_remove(&monitor->monitor_list_head);
751-
free(monitor);
752-
synclist_unlock(&global->processes_table);
753-
return true;
754-
}
755-
}
756-
}
757-
758-
synclist_unlock(&global->processes_table);
759-
return false;
760-
}

src/libAtomVM/globalcontext.h

-11
Original file line numberDiff line numberDiff line change
@@ -532,17 +532,6 @@ Module *globalcontext_get_module(GlobalContext *global, AtomString module_name_a
532532
*/
533533
Module *globalcontext_load_module_from_avm(GlobalContext *global, const char *module_name);
534534

535-
/**
536-
* @brief remove a monitor
537-
*
538-
* @details iterate on the list of all processes and then on each monitor
539-
* to find a given monitor, and remove it
540-
* @param global the global context
541-
* @param ref_ticks the reference to the monitor
542-
* @return true if the monitor was found
543-
*/
544-
bool globalcontext_demonitor(GlobalContext *global, uint64_t ref_ticks);
545-
546535
#ifndef __cplusplus
547536
static inline uint64_t globalcontext_get_ref_ticks(GlobalContext *global)
548537
{

src/libAtomVM/iff.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void scan_iff(const void *iff_binary, int buf_size, unsigned long *offsets, unsi
5050

5151
int current_pos = 12;
5252

53-
uint32_t iff_size = READ_32_ALIGNED(data + 4);
53+
uint32_t iff_size = READ_32_UNALIGNED(data + 4);
5454
int file_size = iff_size;
5555
if (UNLIKELY(buf_size < file_size)) {
5656
fprintf(stderr, "error: buffer holding IFF is smaller than IFF size: %i\n", buf_size);

src/libAtomVM/mailbox.c

+24-3
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ void mailbox_message_dispose(MailboxMessage *m, Heap *heap)
9797
}
9898
case KillSignal:
9999
case TrapAnswerSignal:
100-
case SetGroupLeaderSignal: {
100+
case SetGroupLeaderSignal:
101+
case LinkExitSignal:
102+
case MonitorDownSignal: {
101103
struct TermSignal *term_signal = CONTAINER_OF(m, struct TermSignal, base);
102104
term mso_list = term_signal->storage[STORAGE_MSO_LIST_INDEX];
103105
HeapFragment *fragment = mailbox_message_to_heap_fragment(term_signal, term_signal->heap_end);
@@ -110,12 +112,17 @@ void mailbox_message_dispose(MailboxMessage *m, Heap *heap)
110112
free(request_signal);
111113
break;
112114
}
113-
case TrapExceptionSignal:
114-
case UnlinkSignal: {
115+
case TrapExceptionSignal: {
115116
struct ImmediateSignal *immediate_signal = CONTAINER_OF(m, struct ImmediateSignal, base);
116117
free(immediate_signal);
117118
break;
118119
}
120+
case UnlinkIDSignal:
121+
case UnlinkIDAckSignal: {
122+
struct ImmediateRefSignal *immediate_ref_signal = CONTAINER_OF(m, struct ImmediateRefSignal, base);
123+
free(immediate_ref_signal);
124+
break;
125+
}
119126
case FlushMonitorSignal:
120127
case FlushInfoMonitorSignal:
121128
case DemonitorSignal: {
@@ -308,6 +315,20 @@ void mailbox_send_ref_signal(Context *c, enum MessageType type, uint64_t ref_tic
308315
mailbox_post_message(c, &ref_signal->base);
309316
}
310317

318+
void mailbox_send_immediate_ref_signal(Context *c, enum MessageType type, term immediate, uint64_t ref_ticks)
319+
{
320+
struct ImmediateRefSignal *immediate_ref_signal = malloc(sizeof(struct ImmediateRefSignal));
321+
if (IS_NULL_PTR(immediate_ref_signal)) {
322+
fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__);
323+
return;
324+
}
325+
immediate_ref_signal->base.type = type;
326+
immediate_ref_signal->immediate = immediate;
327+
immediate_ref_signal->ref_ticks = ref_ticks;
328+
329+
mailbox_post_message(c, &immediate_ref_signal->base);
330+
}
331+
311332
void mailbox_send_monitor_signal(Context *c, enum MessageType type, struct Monitor *monitor)
312333
{
313334
struct MonitorPointerSignal *monitor_signal = malloc(sizeof(struct MonitorPointerSignal));

0 commit comments

Comments
 (0)