Skip to content

Commit a00098a

Browse files
committed
Forward port changes from v0.6 release branch
Merge socket refc handling fix (#1477) from release-0.6.
2 parents 6dd7867 + 8e4efd6 commit a00098a

File tree

2 files changed

+43
-27
lines changed

2 files changed

+43
-27
lines changed

src/libAtomVM/globalcontext.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ COLD_FUNC void globalcontext_destroy(GlobalContext *glb)
231231
struct RefcBinary *refc = GET_LIST_ENTRY(item, struct RefcBinary, head);
232232
#ifndef NDEBUG
233233
if (refc->resource_type) {
234-
fprintf(stderr, "Warning, dangling resource of type %s, ref_count = %d\n", refc->resource_type->name, (int) refc->ref_count);
234+
fprintf(stderr, "Warning, dangling resource of type %s, ref_count = %d, data = %p\n", refc->resource_type->name, (int) refc->ref_count, refc->data);
235235
} else {
236236
fprintf(stderr, "Warning, dangling refc binary, ref_count = %d\n", (int) refc->ref_count);
237237
}

src/libAtomVM/otp_socket.c

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <otp_socket.h>
3333
#include <port.h>
3434
#include <posix_nifs.h>
35+
#include <refc_binary.h>
3536
#include <scheduler.h>
3637
#include <smp.h>
3738
#include <sys.h>
@@ -286,6 +287,8 @@ static void socket_stop(ErlNifEnv *caller_env, void *obj, ErlNifEvent event, int
286287
if (rsrc_obj->selecting_process_id != INVALID_PROCESS_ID) {
287288
enif_demonitor_process(caller_env, rsrc_obj, &rsrc_obj->selecting_process_monitor);
288289
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
290+
struct RefcBinary *rsrc_refc = refc_binary_from_data(rsrc_obj);
291+
refc_binary_decrement_refcount(rsrc_refc, caller_env->global);
289292
}
290293

291294
TRACE("socket_stop called on fd=%i\n", rsrc_obj->fd);
@@ -306,42 +309,43 @@ static void socket_down(ErlNifEnv *caller_env, void *obj, ErlNifPid *pid, ErlNif
306309
TRACE("socket_down called on process_id=%i\n", (int) *pid);
307310
#endif
308311

309-
// Increment the reference count so the resource doesn't go away
310-
// (enif_select will decrement the ref count)
311312
struct RefcBinary *rsrc_refc = refc_binary_from_data(rsrc_obj);
312-
refc_binary_increment_refcount(rsrc_refc);
313313
SMP_RWLOCK_WRLOCK(rsrc_obj->socket_lock);
314314

315-
#if OTP_SOCKET_BSD
316-
if (rsrc_obj->selecting_process_id != INVALID_PROCESS_ID) {
317-
// Monitor fired, so make sure we don't try to demonitor in select_stop
318-
// as it could crash trying to reacquire lock on process table
319-
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
320-
enif_select(caller_env, rsrc_obj->fd, ERL_NIF_SELECT_STOP, rsrc_obj, NULL, term_nil());
315+
if (rsrc_obj->selecting_process_id == INVALID_PROCESS_ID) {
316+
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
317+
return;
321318
}
319+
320+
#if OTP_SOCKET_BSD
321+
// Monitor fired, so make sure we don't try to demonitor in select_stop
322+
// as it could crash trying to reacquire lock on process table
323+
// enif_select can decrement ref count but it's at least 2 in this case (1 for monitor and 1 for select)
324+
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
325+
enif_select(caller_env, rsrc_obj->fd, ERL_NIF_SELECT_STOP, rsrc_obj, NULL, term_nil());
322326
#elif OTP_SOCKET_LWIP
323327
// Monitor can be called when we're selecting, accepting or connecting.
324-
if (rsrc_obj->selecting_process_id != INVALID_PROCESS_ID) {
325-
LWIP_BEGIN();
326-
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
327-
if (rsrc_obj->socket_state & SocketStateTCP) {
328-
if (rsrc_obj->socket_state & SocketStateTCPListening) {
329-
(void) tcp_close(rsrc_obj->tcp_pcb);
330-
} else {
331-
tcp_abort(rsrc_obj->tcp_pcb);
332-
}
333-
rsrc_obj->tcp_pcb = NULL;
334-
rsrc_obj->socket_state = SocketStateClosed;
335-
} else if (rsrc_obj->socket_state & SocketStateUDP) {
336-
udp_remove(rsrc_obj->udp_pcb);
337-
rsrc_obj->udp_pcb = NULL;
338-
rsrc_obj->socket_state = SocketStateClosed;
328+
LWIP_BEGIN();
329+
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
330+
if (rsrc_obj->socket_state & SocketStateTCP) {
331+
if (rsrc_obj->socket_state & SocketStateTCPListening) {
332+
(void) tcp_close(rsrc_obj->tcp_pcb);
333+
} else {
334+
tcp_abort(rsrc_obj->tcp_pcb);
339335
}
340-
LWIP_END();
336+
rsrc_obj->tcp_pcb = NULL;
337+
rsrc_obj->socket_state = SocketStateClosed;
338+
} else if (rsrc_obj->socket_state & SocketStateUDP) {
339+
udp_remove(rsrc_obj->udp_pcb);
340+
rsrc_obj->udp_pcb = NULL;
341+
rsrc_obj->socket_state = SocketStateClosed;
341342
}
343+
LWIP_END();
342344
#endif
343345

344346
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
347+
348+
// We're no longer monitoring so we can decrement ref count
345349
refc_binary_decrement_refcount(rsrc_refc, caller_env->global);
346350
}
347351

@@ -949,13 +953,16 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
949953
RAISE_ERROR(BADARG_ATOM);
950954
}
951955

956+
struct RefcBinary *rsrc_refc = refc_binary_from_data(rsrc_obj);
952957
SMP_RWLOCK_WRLOCK(rsrc_obj->socket_lock);
953958

954959
ErlNifEnv *env = erl_nif_env_from_context(ctx);
955960
if (rsrc_obj->selecting_process_id != ctx->process_id && rsrc_obj->selecting_process_id != INVALID_PROCESS_ID) {
956961
// demonitor can fail if process is gone.
957962
enif_demonitor_process(env, rsrc_obj, &rsrc_obj->selecting_process_monitor);
958963
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
964+
// decrement ref count as we are demonitoring
965+
refc_binary_decrement_refcount(rsrc_refc, ctx->global);
959966
}
960967
// Monitor first as select is less likely to fail and it's less expensive to demonitor
961968
// if select fails than to stop select if monitor fails
@@ -964,6 +971,8 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
964971
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
965972
RAISE_ERROR(NOPROC_ATOM);
966973
}
974+
// increment ref count so the resource doesn't go away until monitor is fired
975+
refc_binary_increment_refcount(rsrc_refc);
967976
rsrc_obj->selecting_process_id = ctx->process_id;
968977
}
969978

@@ -980,6 +989,7 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
980989
enif_demonitor_process(env, rsrc_obj, &rsrc_obj->selecting_process_monitor);
981990
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
982991
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
992+
refc_binary_decrement_refcount(rsrc_refc, ctx->global);
983993
RAISE_ERROR(BADARG_ATOM);
984994
}
985995
}
@@ -1026,6 +1036,7 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
10261036
enif_demonitor_process(env, rsrc_obj, &rsrc_obj->selecting_process_monitor);
10271037
LWIP_END();
10281038
SMP_RWLOCK_UNLOCK(rsrc_obj->socket_lock);
1039+
refc_binary_decrement_refcount(rsrc_refc, ctx->global);
10291040
RAISE_ERROR(BADARG_ATOM);
10301041
}
10311042
LWIP_END();
@@ -1048,7 +1059,12 @@ static term nif_socket_select_stop(Context *ctx, int argc, term argv[])
10481059
}
10491060
// Avoid the race condition with select object here.
10501061
SMP_RWLOCK_WRLOCK(rsrc_obj->socket_lock);
1051-
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
1062+
if (rsrc_obj->selecting_process_id != INVALID_PROCESS_ID) {
1063+
enif_demonitor_process(erl_nif_env_from_context(ctx), rsrc_obj, &rsrc_obj->selecting_process_monitor);
1064+
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
1065+
struct RefcBinary *rsrc_refc = refc_binary_from_data(rsrc_obj);
1066+
refc_binary_decrement_refcount(rsrc_refc, ctx->global);
1067+
}
10521068
#if OTP_SOCKET_BSD
10531069
if (UNLIKELY(enif_select(erl_nif_env_from_context(ctx), rsrc_obj->fd, ERL_NIF_SELECT_STOP, rsrc_obj, NULL, term_nil()) < 0)) {
10541070
RAISE_ERROR(BADARG_ATOM);

0 commit comments

Comments
 (0)