32
32
#include <otp_socket.h>
33
33
#include <port.h>
34
34
#include <posix_nifs.h>
35
+ #include <refc_binary.h>
35
36
#include <scheduler.h>
36
37
#include <smp.h>
37
38
#include <sys.h>
@@ -286,6 +287,8 @@ static void socket_stop(ErlNifEnv *caller_env, void *obj, ErlNifEvent event, int
286
287
if (rsrc_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
287
288
enif_demonitor_process (caller_env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
288
289
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 );
289
292
}
290
293
291
294
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
306
309
TRACE ("socket_down called on process_id=%i\n" , (int ) * pid );
307
310
#endif
308
311
309
- // Increment the reference count so the resource doesn't go away
310
- // (enif_select will decrement the ref count)
311
312
struct RefcBinary * rsrc_refc = refc_binary_from_data (rsrc_obj );
312
- refc_binary_increment_refcount (rsrc_refc );
313
313
SMP_RWLOCK_WRLOCK (rsrc_obj -> socket_lock );
314
314
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 ;
321
318
}
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 ());
322
326
#elif OTP_SOCKET_LWIP
323
327
// 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 );
339
335
}
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 ;
341
342
}
343
+ LWIP_END ();
342
344
#endif
343
345
344
346
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
347
+
348
+ // We're no longer monitoring so we can decrement ref count
345
349
refc_binary_decrement_refcount (rsrc_refc , caller_env -> global );
346
350
}
347
351
@@ -949,13 +953,16 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
949
953
RAISE_ERROR (BADARG_ATOM );
950
954
}
951
955
956
+ struct RefcBinary * rsrc_refc = refc_binary_from_data (rsrc_obj );
952
957
SMP_RWLOCK_WRLOCK (rsrc_obj -> socket_lock );
953
958
954
959
ErlNifEnv * env = erl_nif_env_from_context (ctx );
955
960
if (rsrc_obj -> selecting_process_id != ctx -> process_id && rsrc_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
956
961
// demonitor can fail if process is gone.
957
962
enif_demonitor_process (env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
958
963
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 );
959
966
}
960
967
// Monitor first as select is less likely to fail and it's less expensive to demonitor
961
968
// 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[])
964
971
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
965
972
RAISE_ERROR (NOPROC_ATOM );
966
973
}
974
+ // increment ref count so the resource doesn't go away until monitor is fired
975
+ refc_binary_increment_refcount (rsrc_refc );
967
976
rsrc_obj -> selecting_process_id = ctx -> process_id ;
968
977
}
969
978
@@ -980,6 +989,7 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
980
989
enif_demonitor_process (env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
981
990
rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
982
991
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
992
+ refc_binary_decrement_refcount (rsrc_refc , ctx -> global );
983
993
RAISE_ERROR (BADARG_ATOM );
984
994
}
985
995
}
@@ -1026,6 +1036,7 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
1026
1036
enif_demonitor_process (env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
1027
1037
LWIP_END ();
1028
1038
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
1039
+ refc_binary_decrement_refcount (rsrc_refc , ctx -> global );
1029
1040
RAISE_ERROR (BADARG_ATOM );
1030
1041
}
1031
1042
LWIP_END ();
@@ -1048,7 +1059,12 @@ static term nif_socket_select_stop(Context *ctx, int argc, term argv[])
1048
1059
}
1049
1060
// Avoid the race condition with select object here.
1050
1061
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
+ }
1052
1068
#if OTP_SOCKET_BSD
1053
1069
if (UNLIKELY (enif_select (erl_nif_env_from_context (ctx ), rsrc_obj -> fd , ERL_NIF_SELECT_STOP , rsrc_obj , NULL , term_nil ()) < 0 )) {
1054
1070
RAISE_ERROR (BADARG_ATOM );
0 commit comments