Skip to content

Commit abcd02c

Browse files
committed
Merge pull request #2015 from pguyot/w48/ref-resources
Implement resources as references 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 86b2963 + ea45bf0 commit abcd02c

File tree

17 files changed

+489
-96
lines changed

17 files changed

+489
-96
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8282
- `binary_to_integer/1` no longer accepts binaries such as `<<"0xFF">>` or `<<" 123">>`
8383
- `binary_to_integer` and `list_to_integer` do not raise anymore `overflow` error, they raise
8484
instead `badarg`.
85+
- Resources are now references instead of empty binaries.
8586

8687
### Fixed
8788

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

100102
## [0.6.7] - Unreleased
101103

doc/src/differences-with-beam.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,6 @@ locking the VM.
217217

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

220-
### Resources
221-
222-
AtomVM supports resources but for historical reasons these appear as zero-length binaries as they
223-
used to before OTP20, and not as references as they currently do with recent versions of the BEAM.
224-
This has some consequences on matching and serialization.
225-
226220
### BEAM file compatibility
227221

228222
AtomVM can run BEAM files generated by `erlc` compiler from OTP21 to the latest master version,

src/libAtomVM/context.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ bool context_get_process_info(Context *ctx, term *out, size_t *term_size, term a
550550
if (monitor->monitor_type == CONTEXT_MONITOR_MONITORED_LOCAL) {
551551
ret_size += CONS_SIZE;
552552
} else if (monitor->monitor_type == CONTEXT_MONITOR_RESOURCE) {
553-
ret_size += CONS_SIZE + TERM_BOXED_RESOURCE_SIZE;
553+
ret_size += CONS_SIZE + TERM_BOXED_REFERENCE_RESOURCE_SIZE;
554554
}
555555
}
556556
break;

src/libAtomVM/dist_nifs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ static term nif_erlang_setnode_3(Context *ctx, int argc, term argv[])
349349
}
350350
synclist_unlock(&ctx->global->dist_connections);
351351

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

706706
static void dist_net_kernel_send_connect(term net_kernel_proc, struct DistConnection *new_conn_obj, int node_atom_index, Context *ctx)
707707
{
708-
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + TERM_BOXED_RESOURCE_SIZE, heap)
708+
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + TERM_BOXED_REFERENCE_RESOURCE_SIZE, heap)
709709
term autoconnect_message = term_alloc_tuple(3, &heap);
710710
term_put_tuple_element(autoconnect_message, 0, CONNECT_ATOM);
711711
term_put_tuple_element(autoconnect_message, 1, term_from_atom_index(node_atom_index));

src/libAtomVM/externalterm.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "defaultatoms.h"
3333
#include "memory.h"
3434
#include "module.h"
35+
#include "resources.h"
3536
#include "term.h"
3637
#include "unicode.h"
3738
#include "utils.h"
@@ -488,20 +489,38 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb)
488489
buf[0] = NEWER_REFERENCE_EXT;
489490
}
490491
size_t k = 1;
491-
uint32_t len = 2;
492+
uint32_t len;
493+
if (term_is_resource_reference(t)) {
494+
len = 4;
495+
} else {
496+
len = 2;
497+
}
492498
if (!IS_NULL_PTR(buf)) {
493499
WRITE_16_UNALIGNED(buf + k, len);
494500
}
495501
k += 2;
496502
term node_name = glb->node_name;
497503
uint32_t creation = node_name == NONODE_AT_NOHOST_ATOM ? 0 : glb->creation;
498504
k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, node_name, glb);
499-
if (!IS_NULL_PTR(buf)) {
500-
uint64_t ticks = term_to_ref_ticks(t);
501-
WRITE_32_UNALIGNED(buf + k, creation);
502-
WRITE_64_UNALIGNED(buf + k + 4, ticks);
505+
if (term_is_resource_reference(t)) {
506+
if (!IS_NULL_PTR(buf)) {
507+
WRITE_32_UNALIGNED(buf + k, creation);
508+
struct RefcBinary *refc_binary = term_resource_refc_binary_ptr(t);
509+
struct ResourceType *resource_type = refc_binary->resource_type;
510+
void *resource = refc_binary->data;
511+
uint64_t serialize_ref = resource_serialize(resource, resource_type);
512+
WRITE_64_UNALIGNED(buf + k + 4, ((uintptr_t) resource_type));
513+
WRITE_64_UNALIGNED(buf + k + 12, ((uintptr_t) serialize_ref));
514+
}
515+
return k + 20;
516+
} else {
517+
if (!IS_NULL_PTR(buf)) {
518+
uint64_t ticks = term_to_ref_ticks(t);
519+
WRITE_32_UNALIGNED(buf + k, creation);
520+
WRITE_64_UNALIGNED(buf + k + 4, ticks);
521+
}
522+
return k + 12;
503523
}
504-
return k + 12;
505524
} else if (term_is_external_reference(t)) {
506525
if (!IS_NULL_PTR(buf)) {
507526
buf[0] = NEWER_REFERENCE_EXT;
@@ -882,6 +901,11 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm
882901
if (len == 2 && node == this_node && creation == this_creation) {
883902
uint64_t ticks = ((uint64_t) data[0]) << 32 | data[1];
884903
return term_from_ref_ticks(ticks, heap);
904+
} else if (len == 4 && node == this_node && creation == this_creation) {
905+
// This is a resource
906+
uint64_t resource_type_ptr = ((uint64_t) data[0]) << 32 | data[1];
907+
uint64_t resource_serialize_ref = ((uint64_t) data[2]) << 32 | data[3];
908+
return term_from_resource_type_and_serialize_ref(resource_type_ptr, resource_serialize_ref, heap, glb);
885909
} else {
886910
return term_make_external_reference(node, len, data, creation, heap);
887911
}
@@ -1294,8 +1318,12 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini
12941318
}
12951319
if (external_term_buf[3] == SMALL_ATOM_UTF8_EXT) {
12961320
// Check if it's non-distributed node, in which case it's always a local ref
1297-
if (len == 2 && external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) {
1298-
heap_size = REF_SIZE;
1321+
if (external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) {
1322+
if (len == 2) {
1323+
heap_size = REF_SIZE;
1324+
} else if (len == 4) {
1325+
heap_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE;
1326+
}
12991327
}
13001328
// See above for pids
13011329
} else if (UNLIKELY(external_term_buf[3] != ATOM_EXT)) {

src/libAtomVM/memory.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,11 @@ static void memory_scan_and_copy(HeapFragment *old_fragment, term *mem_start, co
616616

617617
case TERM_BOXED_REF:
618618
TRACE("- Found ref.\n");
619+
term ref = ((term) ptr) | TERM_PRIMARY_BOXED;
620+
if (term_is_resource_reference(ref)) {
621+
*mso_list = term_list_init_prepend(ptr + REFERENCE_RESOURCE_CONS_OFFSET, ref, *mso_list);
622+
refc_binary_increment_refcount((struct RefcBinary *) term_resource_refc_binary_ptr(ref));
623+
}
619624
break;
620625

621626
case TERM_BOXED_EXTERNAL_PID:
@@ -953,6 +958,18 @@ void memory_sweep_mso_list(term mso_list, GlobalContext *global, bool from_task)
953958
refc_binary_decrement_refcount(term_refc_binary_ptr(h), global);
954959
#ifdef AVM_TASK_DRIVER_ENABLED
955960
}
961+
#endif
962+
}
963+
if (term_is_resource_reference(h)) {
964+
// unreferenced binary; decrement reference count
965+
#ifdef AVM_TASK_DRIVER_ENABLED
966+
if (from_task) {
967+
globalcontext_refc_decrement_refcount_from_task(global, term_resource_refc_binary_ptr(h));
968+
} else {
969+
#endif
970+
refc_binary_decrement_refcount(term_resource_refc_binary_ptr(h), global);
971+
#ifdef AVM_TASK_DRIVER_ENABLED
972+
}
956973
#endif
957974
}
958975
l = term_get_list_tail(l);

src/libAtomVM/otp_socket.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ static const AtomStringIntPair otp_socket_setopt_level_table[] = {
245245

246246
static ErlNifResourceType *socket_resource_type;
247247

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

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

635-
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_RESOURCE_SIZE) != MEMORY_GC_OK)) {
635+
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE) != MEMORY_GC_OK)) {
636636
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
637637
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
638638
}
@@ -675,7 +675,7 @@ bool term_is_otp_socket(term socket_term)
675675
{
676676
bool ret = term_is_tuple(socket_term)
677677
&& term_get_tuple_arity(socket_term) == 2
678-
&& term_is_binary(term_get_tuple_element(socket_term, 0))
678+
&& term_is_resource_reference(term_get_tuple_element(socket_term, 0))
679679
&& term_is_local_reference(term_get_tuple_element(socket_term, 1));
680680

681681
TRACE("term is a socket: %i\n", ret);
@@ -1929,7 +1929,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[])
19291929
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
19301930
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
19311931
}
1932-
size_t requested_size = TERM_BOXED_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
1932+
size_t requested_size = TERM_BOXED_REFERENCE_RESOURCE_SIZE + TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
19331933
if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
19341934
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
19351935
LWIP_END();

src/libAtomVM/otp_ssl.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ static term nif_ssl_entropy_init(Context *ctx, int argc, term argv[])
226226
UNUSED(argc);
227227
UNUSED(argv);
228228

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

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

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

357-
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_RESOURCE_SIZE) != MEMORY_GC_OK)) {
357+
if (UNLIKELY(memory_ensure_free(ctx, TERM_BOXED_REFERENCE_RESOURCE_SIZE) != MEMORY_GC_OK)) {
358358
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
359359
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
360360
}

src/libAtomVM/posix_nifs.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ static term nif_atomvm_posix_open(Context *ctx, int argc, term argv[])
348348
term_put_tuple_element(result, 0, ERROR_ATOM);
349349
term_put_tuple_element(result, 1, posix_errno_to_term(errno, glb));
350350
} else {
351-
if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(2) + TERM_BOXED_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
351+
if (UNLIKELY(memory_ensure_free_opt(ctx, TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_RESOURCE_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
352352
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
353353
}
354354
term obj = make_posix_fd_resource(ctx, fd);
@@ -672,7 +672,7 @@ static term nif_atomvm_subprocess(Context *ctx, int argc, term argv[])
672672
free_string_list(args);
673673
free_string_list(envp_array);
674674

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

@@ -846,7 +846,7 @@ static term nif_atomvm_posix_opendir(Context *ctx, int argc, term argv[])
846846
}
847847
dir_obj->dir = dir;
848848
if (UNLIKELY(memory_ensure_free_opt(
849-
ctx, TUPLE_SIZE(2) + TERM_BOXED_RESOURCE_SIZE, MEMORY_CAN_SHRINK)
849+
ctx, TUPLE_SIZE(2) + TERM_BOXED_REFERENCE_RESOURCE_SIZE, MEMORY_CAN_SHRINK)
850850
!= MEMORY_GC_OK)) {
851851
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
852852
}

src/libAtomVM/refc_binary.c

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,24 @@ void refc_binary_increment_refcount(struct RefcBinary *refc)
6666
bool refc_binary_decrement_refcount(struct RefcBinary *refc, struct GlobalContext *global)
6767
{
6868
if (--refc->ref_count == 0) {
69-
if (refc->resource_type && refc->resource_type->down) {
70-
// There may be monitors associated with this resource.
71-
destroy_resource_monitors(refc, global);
72-
// After this point, the resource can no longer be found by
73-
// resource_type_fire_monitor
74-
// However, resource_type_fire_monitor may have incremented ref_count
75-
// to call the monitor handler.
76-
// So we check ref_count again. We're not affected by the ABA problem
77-
// here as the resource cannot (should not) be monitoring while it is
78-
// being destroyed, i.e. no resource monitor will be created now
79-
if (UNLIKELY(refc->ref_count != 0)) {
80-
return false;
69+
if (refc->resource_type) {
70+
if (refc->resource_type->down) {
71+
// There may be monitors associated with this resource.
72+
destroy_resource_monitors(refc, global);
73+
// After this point, the resource can no longer be found by
74+
// resource_type_fire_monitor
75+
// However, resource_type_fire_monitor may have incremented ref_count
76+
// to call the monitor handler.
77+
// So we check ref_count again. We're not affected by the ABA problem
78+
// here as the resource cannot (should not) be monitoring while it is
79+
// being destroyed, i.e. no resource monitor will be created now
80+
if (UNLIKELY(refc->ref_count != 0)) {
81+
return false;
82+
}
8183
}
84+
// Remove the resource from the list of serialized resources
85+
// so it no longer can be unserialized.
86+
resource_unmark_serialized(refc->data, refc->resource_type);
8287
}
8388
synclist_remove(&global->refc_binaries, &refc->head);
8489
refc_binary_destroy(refc, global);

0 commit comments

Comments
 (0)