Skip to content

Commit a2cef17

Browse files
committed
Clarify enif_make_resource usage
Also fix a couple of possible memory leaks in error paths Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 1cb7e56 commit a2cef17

File tree

7 files changed

+41
-15
lines changed

7 files changed

+41
-15
lines changed

src/libAtomVM/erl_nif.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ ErlNifResourceType *enif_init_resource_type(ErlNifEnv *env, const char *name, co
150150

151151
/**
152152
* @brief Allocate a resource for given type for `size` bytes.
153+
* @details following BEAM semantics, the resource is created with a refcount
154+
* of 1. A call to `enif_release_resource` will decrement the refcount and
155+
* destroy the resource if it is zero.
156+
*
157+
* Typical usage (as suggested by BEAM documentation) is to call
158+
* `enif_make_resource` and then `enif_release_resource` to somewhat transfer
159+
* ownership to the garbage collector. `enif_make_resource` will increment
160+
* refcount to 2 and also add the resource to the MSO list of the context, so
161+
* when the term is no longer referenced in the context heap, the reference
162+
* counter will be decremented by gc.
163+
*
153164
* @param type a trype created by `enif_init_resource_type`.
154165
* @param size the size in bytes.
155166
* @return a pointer or `NULL` on failure.
@@ -184,9 +195,18 @@ int enif_release_resource(void *resource);
184195
/**
185196
* @brief create a term from a resource
186197
* @details the term can be later passed to `enif_get_resource`.
187-
* The resource is typically released (by calling `enif_release_resource`)
188-
* just after calling this function to "transfer ownership" to Erlang code so
189-
* that it will be destroyed when garbage collected.
198+
*
199+
* The resource reference counter is incremented and it is added to the MSO
200+
* list of the heap of env (which must be a context).
201+
*
202+
* If the resource was just allocated with `enif_alloc_resource`, the reference
203+
* counter should typically be decremented by a call to `enif_release_resource`
204+
* matching usage documented by BEAM.
205+
*
206+
* If the resource was not just allocated with `enif_alloc_resource`, to clear
207+
* usage confusion, users should rather call `term_from_resource` and should
208+
* not decrement the reference counter.
209+
*
190210
* @param env current environment
191211
* @param obj resource
192212
* @return a new term representing the resource

src/libAtomVM/otp_socket.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
529529
#ifndef AVM_NO_SMP
530530
rsrc_obj->socket_lock = smp_rwlock_create();
531531
if (IS_NULL_PTR(rsrc_obj->socket_lock)) {
532+
// destroy resource object without calling dtor
532533
free(rsrc_obj);
533534
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
534535
}
@@ -537,6 +538,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
537538
rsrc_obj->fd = socket(domain, type, protocol);
538539
if (UNLIKELY(rsrc_obj->fd == -1 || rsrc_obj->fd == CLOSED_FD)) {
539540
AVM_LOGE(TAG, "Failed to initialize socket.");
541+
rsrc_obj->fd = CLOSED_FD;
540542
enif_release_resource(rsrc_obj);
541543
return make_errno_tuple(ctx);
542544
} else {
@@ -555,6 +557,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
555557
LWIP_END();
556558
rsrc_obj->socket_state = SocketStateUDPIdle;
557559
} else {
560+
rsrc_obj->socket_state = SocketStateClosed;
558561
enif_release_resource(rsrc_obj);
559562
RAISE_ERROR(BADARG_ATOM);
560563
}
@@ -586,7 +589,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
586589
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
587590
}
588591
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
589-
enif_release_resource(rsrc_obj);
592+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
590593

591594
size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
592595
if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &obj, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
@@ -788,6 +791,7 @@ static struct SocketResource *make_accepted_socket_resource(struct tcp_pcb *newp
788791
#ifndef AVM_NO_SMP
789792
conn_rsrc_obj->socket_lock = smp_rwlock_create();
790793
if (IS_NULL_PTR(conn_rsrc_obj->socket_lock)) {
794+
// destroy resource without calling destructor
791795
free(conn_rsrc_obj);
792796
return NULL;
793797
}
@@ -1563,7 +1567,7 @@ static term nif_socket_listen(Context *ctx, int argc, term argv[])
15631567
static term make_accepted_socket_term(Context *ctx, struct SocketResource *conn_rsrc_obj)
15641568
{
15651569
term obj = enif_make_resource(erl_nif_env_from_context(ctx), conn_rsrc_obj);
1566-
enif_release_resource(conn_rsrc_obj);
1570+
enif_release_resource(conn_rsrc_obj); // decrement refcount after enif_allocate_resource in make_accepted_socket_resource
15671571

15681572
term socket_term = term_alloc_tuple(2, &ctx->heap);
15691573
uint64_t ref_ticks = globalcontext_get_ref_ticks(ctx->global);
@@ -1636,7 +1640,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[])
16361640
TRACE("nif_socket_accept: Created socket on accept fd=%i\n", rsrc_obj->fd);
16371641

16381642
term new_resource = enif_make_resource(erl_nif_env_from_context(ctx), conn_rsrc_obj);
1639-
enif_release_resource(conn_rsrc_obj);
1643+
enif_release_resource(conn_rsrc_obj); // decrement refcount after enif_alloc_resource
16401644

16411645
size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(2) + REF_SIZE;
16421646
if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 1, &new_resource, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
@@ -1673,6 +1677,7 @@ static term nif_socket_accept(Context *ctx, int argc, term argv[])
16731677
AVM_LOGW(TAG, "Failed to allocate memory: %s:%i.", __FILE__, __LINE__);
16741678
LWIP_END();
16751679
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
1680+
enif_release_resource(new_resource);
16761681
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
16771682
}
16781683

src/libAtomVM/otp_ssl.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ static term nif_ssl_entropy_init(Context *ctx, int argc, term argv[])
236236
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
237237
}
238238
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
239-
enif_release_resource(rsrc_obj);
239+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
240240

241241
mbedtls_entropy_init(&rsrc_obj->context);
242242

@@ -259,7 +259,7 @@ static term nif_ssl_ctr_drbg_init(Context *ctx, int argc, term argv[])
259259
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
260260
}
261261
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
262-
enif_release_resource(rsrc_obj);
262+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
263263

264264
mbedtls_ctr_drbg_init(&rsrc_obj->context);
265265

@@ -311,7 +311,7 @@ static term nif_ssl_init(Context *ctx, int argc, term argv[])
311311
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
312312
}
313313
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
314-
enif_release_resource(rsrc_obj);
314+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
315315

316316
mbedtls_ssl_init(&rsrc_obj->context);
317317

@@ -364,7 +364,7 @@ static term nif_ssl_config_init(Context *ctx, int argc, term argv[])
364364
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
365365
}
366366
term obj = enif_make_resource(erl_nif_env_from_context(ctx), rsrc_obj);
367-
enif_release_resource(rsrc_obj);
367+
enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource
368368

369369
mbedtls_ssl_config_init(&rsrc_obj->config);
370370

src/libAtomVM/posix_nifs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ static term nif_atomvm_posix_open(Context *ctx, int argc, term argv[])
315315
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
316316
}
317317
term obj = enif_make_resource(erl_nif_env_from_context(ctx), fd_obj);
318-
enif_release_resource(fd_obj);
318+
enif_release_resource(fd_obj); // decrement refcount after enif_alloc_resource
319319
result = term_alloc_tuple(2, &ctx->heap);
320320
term_put_tuple_element(result, 0, OK_ATOM);
321321
term_put_tuple_element(result, 1, obj);
@@ -680,7 +680,7 @@ static term nif_atomvm_posix_opendir(Context *ctx, int argc, term argv[])
680680
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
681681
}
682682
term obj = enif_make_resource(erl_nif_env_from_context(ctx), dir_obj);
683-
enif_release_resource(dir_obj);
683+
enif_release_resource(dir_obj); // decrement refcount after enif_alloc_resource
684684
result = term_alloc_tuple(2, &ctx->heap);
685685
term_put_tuple_element(result, 0, OK_ATOM);
686686
term_put_tuple_element(result, 1, obj);

src/platforms/emscripten/src/lib/platform_nifs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ static EM_BOOL html5api_touch_callback(int eventType, const EmscriptenTouchEvent
677677
resource->event = event_constant; \
678678
EMSCRIPTEN_RESULT result = emscripten_set_##callback##_callback_on_thread(target, resource, use_capture, html5api_##event_type##_callback, emscripten_main_runtime_thread_id()); \
679679
if (result != EMSCRIPTEN_RESULT_SUCCESS && result != EMSCRIPTEN_RESULT_DEFERRED) { \
680+
enif_release_resource(resource); \
680681
return term_from_emscripten_result(result, ctx); \
681682
} \
682683
term resource_term = enif_make_resource(erl_nif_env_from_context(ctx), resource); \

src/platforms/esp32/components/avm_builtins/adc_driver.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ static term nif_adc_init(Context *ctx, int argc, term argv[])
349349
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
350350
}
351351
ERL_NIF_TERM unit_obj = enif_make_resource(erl_nif_env_from_context(ctx), unit_rsrc);
352-
enif_release_resource(unit_rsrc);
352+
enif_release_resource(unit_rsrc); // decrement refcount after enif_alloc_resource
353353

354354
// {ok, {'$adc', Unit :: resource(), ref()}}
355355
size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE;
@@ -501,7 +501,7 @@ static term nif_adc_acquire(Context *ctx, int argc, term argv[])
501501
}
502502

503503
term chan_obj = enif_make_resource(erl_nif_env_from_context(ctx), chan_rsrc);
504-
enif_release_resource(chan_rsrc);
504+
enif_release_resource(chan_rsrc); // decrement refcount after enif_alloc_resource
505505

506506
// {ok, {'$adc', resource(), ref()}}
507507
size_t requested_size = TUPLE_SIZE(2) + TUPLE_SIZE(3) + REF_SIZE;

src/platforms/esp32/components/avm_builtins/storage_nif.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ static term nif_esp_mount(Context *ctx, int argc, term argv[])
270270
term_put_tuple_element(return_term, 0, OK_ATOM);
271271
term_put_tuple_element(return_term, 1, mount_term);
272272
}
273-
enif_release_resource(mount);
273+
enif_release_resource(mount); // decrement refcount after either enif_alloc_resource
274274

275275
return return_term;
276276
}

0 commit comments

Comments
 (0)