Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `binary_to_integer/1` no longer accepts binaries such as `<<"0xFF">>` or `<<" 123">>`
- `binary_to_integer` and `list_to_integer` do not raise anymore `overflow` error, they raise
instead `badarg`.
- Resources are now references instead of empty binaries.

### Fixed

Expand All @@ -95,6 +96,7 @@ instead `badarg`.
- Added missing support for supervisor `one_for_all` strategy.
- Supervisor now honors period and intensity options.
- Fix supervisor crash if a `one_for_one` child fails to restart.
- Fix collision in references created with `make_ref/0` on 32 bits platforms.

## [0.6.7] - Unreleased

Expand Down
6 changes: 0 additions & 6 deletions doc/src/differences-with-beam.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,6 @@ locking the VM.

Ports are also executed by the schedulers and should return quickly.

### Resources

AtomVM supports resources but for historical reasons these appear as zero-length binaries as they
used to before OTP20, and not as references as they currently do with recent versions of the BEAM.
This has some consequences on matching and serialization.

### BEAM file compatibility

AtomVM can run BEAM files generated by `erlc` compiler from OTP21 to the latest master version,
Expand Down
2 changes: 1 addition & 1 deletion src/libAtomVM/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ bool context_get_process_info(Context *ctx, term *out, size_t *term_size, term a
if (monitor->monitor_type == CONTEXT_MONITOR_MONITORED_LOCAL) {
ret_size += CONS_SIZE;
} else if (monitor->monitor_type == CONTEXT_MONITOR_RESOURCE) {
ret_size += CONS_SIZE + TERM_BOXED_RESOURCE_SIZE;
ret_size += CONS_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE;
}
}
break;
Expand Down
4 changes: 2 additions & 2 deletions src/libAtomVM/dist_nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ static term nif_erlang_setnode_3(Context *ctx, int argc, term argv[])
}
synclist_unlock(&ctx->global->dist_connections);

if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free_opt(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
term obj = term_from_resource(conn_obj, &ctx->heap);
Expand Down Expand Up @@ -705,7 +705,7 @@ static term dist_get_net_kernel_and_create_connection(struct DistConnection **co

static void dist_net_kernel_send_connect(term net_kernel_proc, struct DistConnection *new_conn_obj, int node_atom_index, Context *ctx)
{
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + TERM_BOXED_RESOURCE_SIZE, heap)
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_RESOURCE_SIZE, heap)
term autoconnect_message = term_alloc_tuple(3, &heap);
term_put_tuple_element(autoconnect_message, 0, CONNECT_ATOM);
term_put_tuple_element(autoconnect_message, 1, term_from_atom_index(node_atom_index));
Expand Down
44 changes: 36 additions & 8 deletions src/libAtomVM/externalterm.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "defaultatoms.h"
#include "memory.h"
#include "module.h"
#include "resources.h"
#include "term.h"
#include "unicode.h"
#include "utils.h"
Expand Down Expand Up @@ -488,20 +489,38 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb)
buf[0] = NEWER_REFERENCE_EXT;
}
size_t k = 1;
uint32_t len = 2;
uint32_t len;
if (term_is_reference_resource(t)) {
len = 4;
} else {
len = 2;
}
if (!IS_NULL_PTR(buf)) {
WRITE_16_UNALIGNED(buf + k, len);
}
k += 2;
term node_name = glb->node_name;
uint32_t creation = node_name == NONODE_AT_NOHOST_ATOM ? 0 : glb->creation;
k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, node_name, glb);
if (!IS_NULL_PTR(buf)) {
uint64_t ticks = term_to_ref_ticks(t);
WRITE_32_UNALIGNED(buf + k, creation);
WRITE_64_UNALIGNED(buf + k + 4, ticks);
if (term_is_reference_resource(t)) {
if (!IS_NULL_PTR(buf)) {
WRITE_32_UNALIGNED(buf + k, creation);
struct RefcBinary *refc_binary = term_resource_refc_binary_ptr(t);
struct ResourceType *resource_type = refc_binary->resource_type;
void *resource = refc_binary->data;
resource_mark_serialized(resource, resource_type);
WRITE_64_UNALIGNED(buf + k + 4, ((uintptr_t) resource_type));
WRITE_64_UNALIGNED(buf + k + 12, ((uintptr_t) resource));
}
return k + 20;
} else {
if (!IS_NULL_PTR(buf)) {
uint64_t ticks = term_to_ref_ticks(t);
WRITE_32_UNALIGNED(buf + k, creation);
WRITE_64_UNALIGNED(buf + k + 4, ticks);
}
return k + 12;
}
return k + 12;
} else if (term_is_external_reference(t)) {
if (!IS_NULL_PTR(buf)) {
buf[0] = NEWER_REFERENCE_EXT;
Expand Down Expand Up @@ -882,6 +901,11 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm
if (len == 2 && node == this_node && creation == this_creation) {
uint64_t ticks = ((uint64_t) data[0]) << 32 | data[1];
return term_from_ref_ticks(ticks, heap);
} else if (len == 4 && node == this_node && creation == this_creation) {
// This is a resource
uint64_t resource_type_ptr = ((uint64_t) data[0]) << 32 | data[1];
uint64_t resource_data_ptr = ((uint64_t) data[2]) << 32 | data[3];
return term_from_resource_type_and_data(resource_type_ptr, resource_data_ptr, heap, glb);
} else {
return term_make_external_reference(node, len, data, creation, heap);
}
Expand Down Expand Up @@ -1294,8 +1318,12 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini
}
if (external_term_buf[3] == SMALL_ATOM_UTF8_EXT) {
// Check if it's non-distributed node, in which case it's always a local ref
if (len == 2 && external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) {
heap_size = REF_SIZE;
if (external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) {
if (len == 2) {
heap_size = REF_SIZE;
} else if (len == 4) {
heap_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE;
}
}
// See above for pids
} else if (UNLIKELY(external_term_buf[3] != ATOM_EXT)) {
Expand Down
17 changes: 17 additions & 0 deletions src/libAtomVM/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,11 @@ static void memory_scan_and_copy(HeapFragment *old_fragment, term *mem_start, co

case TERM_BOXED_REF:
TRACE("- Found ref.\n");
term ref = ((term) ptr) | TERM_PRIMARY_BOXED;
if (term_is_reference_resource(ref)) {
*mso_list = term_list_init_prepend(ptr + REFERENCE_RESOURCE_CONS_OFFSET, ref, *mso_list);
refc_binary_increment_refcount((struct RefcBinary *) term_resource_refc_binary_ptr(ref));
}
break;

case TERM_BOXED_EXTERNAL_PID:
Expand Down Expand Up @@ -953,6 +958,18 @@ void memory_sweep_mso_list(term mso_list, GlobalContext *global, bool from_task)
refc_binary_decrement_refcount(term_refc_binary_ptr(h), global);
#ifdef AVM_TASK_DRIVER_ENABLED
}
#endif
}
if (term_is_reference_resource(h)) {
// unreferenced binary; decrement reference count
#ifdef AVM_TASK_DRIVER_ENABLED
if (from_task) {
globalcontext_refc_decrement_refcount_from_task(global, term_resource_refc_binary_ptr(h));
} else {
#endif
refc_binary_decrement_refcount(term_resource_refc_binary_ptr(h), global);
#ifdef AVM_TASK_DRIVER_ENABLED
}
#endif
}
l = term_get_list_tail(l);
Expand Down
8 changes: 4 additions & 4 deletions src/libAtomVM/otp_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ static const AtomStringIntPair otp_socket_setopt_level_table[] = {

static ErlNifResourceType *socket_resource_type;

#define SOCKET_MAKE_SELECT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + REF_SIZE + TUPLE_SIZE(2) + REF_SIZE + TERM_BOXED_RESOURCE_SIZE)
#define SOCKET_MAKE_SELECT_NOTIFICATION_SIZE (TUPLE_SIZE(4) + REF_SIZE + TUPLE_SIZE(2) + REF_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE)
static term socket_make_select_notification(struct SocketResource *rsrc_obj, Heap *heap);

//
Expand Down Expand Up @@ -632,7 +632,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
#endif
rsrc_obj->buf_size = DEFAULT_BUFFER_SIZE;

if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_RESOURCE_SIZE) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE) != MEMORY_GC_OK)) {
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
Expand Down Expand Up @@ -675,7 +675,7 @@ bool term_is_otp_socket(term socket_term)
{
bool ret = term_is_tuple(socket_term)
&& term_get_tuple_arity(socket_term) == 2
&& term_is_binary(term_get_tuple_element(socket_term, 0))
&& term_is_reference_resource(term_get_tuple_element(socket_term, 0))
&& term_is_local_reference(term_get_tuple_element(socket_term, 1));

TRACE("term is a socket: %i\n", ret);
Expand Down Expand Up @@ -1929,7 +1929,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[])
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
size_t requested_size = TERM_BOXED_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
size_t requested_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
LWIP_END();
Expand Down
8 changes: 4 additions & 4 deletions src/libAtomVM/otp_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ static term nif_ssl_entropy_init(Context *ctx, int argc, term argv[])
UNUSED(argc);
UNUSED(argv);

if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_RESOURCE_SIZE) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE) != MEMORY_GC_OK)) {
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
Expand All @@ -249,7 +249,7 @@ static term nif_ssl_ctr_drbg_init(Context *ctx, int argc, term argv[])
UNUSED(argc);
UNUSED(argv);

if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_RESOURCE_SIZE) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE) != MEMORY_GC_OK)) {
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
Expand Down Expand Up @@ -301,7 +301,7 @@ static term nif_ssl_init(Context *ctx, int argc, term argv[])
UNUSED(argc);
UNUSED(argv);

if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_RESOURCE_SIZE) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE) != MEMORY_GC_OK)) {
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
Expand Down Expand Up @@ -354,7 +354,7 @@ static term nif_ssl_config_init(Context *ctx, int argc, term argv[])
UNUSED(argc);
UNUSED(argv);

if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_RESOURCE_SIZE) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE) != MEMORY_GC_OK)) {
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
Expand Down
6 changes: 3 additions & 3 deletions src/libAtomVM/posix_nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ static term nif_atomvm_posix_open(Context *ctx, int argc, term argv[])
term_put_tuple_element(result, 0, ERROR_ATOM);
term_put_tuple_element(result, 1, posix_errno_to_term(errno, glb));
} else {
if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(2) + TERM_BOXED_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
term obj = make_posix_fd_resource(ctx, fd);
Expand Down Expand Up @@ -672,7 +672,7 @@ static term nif_atomvm_subprocess(Context *ctx, int argc, term argv[])
free_string_list(args);
free_string_list(envp_array);

if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(3) + TERM_BOXED_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}

Expand Down Expand Up @@ -846,7 +846,7 @@ static term nif_atomvm_posix_opendir(Context *ctx, int argc, term argv[])
}
dir_obj->dir = dir;
if (UNLIKELY(memory_ensure_free_opt(
ctx, TUPLE_SIZE(2) + TERM_BOXED_RESOURCE_SIZE, MEMORY_CAN_SHRINK)
ctx, TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_RESOURCE_SIZE, MEMORY_CAN_SHRINK)
!= MEMORY_GC_OK)) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
Expand Down
29 changes: 17 additions & 12 deletions src/libAtomVM/refc_binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,24 @@ void refc_binary_increment_refcount(struct RefcBinary *refc)
bool refc_binary_decrement_refcount(struct RefcBinary *refc, struct GlobalContext *global)
{
if (--refc->ref_count == 0) {
if (refc->resource_type && refc->resource_type->down) {
// There may be monitors associated with this resource.
destroy_resource_monitors(refc, global);
// After this point, the resource can no longer be found by
// resource_type_fire_monitor
// However, resource_type_fire_monitor may have incremented ref_count
// to call the monitor handler.
// So we check ref_count again. We're not affected by the ABA problem
// here as the resource cannot (should not) be monitoring while it is
// being destroyed, i.e. no resource monitor will be created now
if (UNLIKELY(refc->ref_count != 0)) {
return false;
if (refc->resource_type) {
if (refc->resource_type->down) {
// There may be monitors associated with this resource.
destroy_resource_monitors(refc, global);
// After this point, the resource can no longer be found by
// resource_type_fire_monitor
// However, resource_type_fire_monitor may have incremented ref_count
// to call the monitor handler.
// So we check ref_count again. We're not affected by the ABA problem
// here as the resource cannot (should not) be monitoring while it is
// being destroyed, i.e. no resource monitor will be created now
if (UNLIKELY(refc->ref_count != 0)) {
return false;
}
}
// Remove the resource from the list of serialized resources
// so it no longer can be unserialized.
resource_unmark_serialized(refc->data, refc->resource_type);
}
synclist_remove(&global->refc_binaries, &refc->head);
refc_binary_destroy(refc, global);
Expand Down
Loading
Loading