Skip to content

Commit 74fe76e

Browse files
committed
Merge pull request #1617 from pguyot/w14/clarify-enif_make_resource-usage
Clarify enif_make_resource usage Also fix a couple of possible memory leaks in error paths 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 87441e6 + a2cef17 commit 74fe76e

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)