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>
@@ -285,6 +286,8 @@ static void socket_stop(ErlNifEnv *caller_env, void *obj, ErlNifEvent event, int
285
286
if (rsrc_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
286
287
enif_demonitor_process (caller_env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
287
288
rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
289
+ struct RefcBinary * rsrc_refc = refc_binary_from_data (rsrc_obj );
290
+ refc_binary_decrement_refcount (rsrc_refc , caller_env -> global );
288
291
}
289
292
290
293
TRACE ("socket_stop called on fd=%i\n" , rsrc_obj -> fd );
@@ -305,42 +308,43 @@ static void socket_down(ErlNifEnv *caller_env, void *obj, ErlNifPid *pid, ErlNif
305
308
TRACE ("socket_down called on process_id=%i\n" , (int ) * pid );
306
309
#endif
307
310
308
- // Increment the reference count so the resource doesn't go away
309
- // (enif_select will decrement the ref count)
310
311
struct RefcBinary * rsrc_refc = refc_binary_from_data (rsrc_obj );
311
- refc_binary_increment_refcount (rsrc_refc );
312
312
SMP_RWLOCK_WRLOCK (rsrc_obj -> socket_lock );
313
313
314
- #if OTP_SOCKET_BSD
315
- if (rsrc_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
316
- // Monitor fired, so make sure we don't try to demonitor in select_stop
317
- // as it could crash trying to reacquire lock on process table
318
- rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
319
- enif_select (caller_env , rsrc_obj -> fd , ERL_NIF_SELECT_STOP , rsrc_obj , NULL , term_nil ());
314
+ if (rsrc_obj -> selecting_process_id == INVALID_PROCESS_ID ) {
315
+ SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
316
+ return ;
320
317
}
318
+
319
+ #if OTP_SOCKET_BSD
320
+ // Monitor fired, so make sure we don't try to demonitor in select_stop
321
+ // as it could crash trying to reacquire lock on process table
322
+ // enif_select can decrement ref count but it's at least 2 in this case (1 for monitor and 1 for select)
323
+ rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
324
+ enif_select (caller_env , rsrc_obj -> fd , ERL_NIF_SELECT_STOP , rsrc_obj , NULL , term_nil ());
321
325
#elif OTP_SOCKET_LWIP
322
326
// Monitor can be called when we're selecting, accepting or connecting.
323
- if (rsrc_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
324
- LWIP_BEGIN ();
325
- rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
326
- if (rsrc_obj -> socket_state & SocketStateTCP ) {
327
- if (rsrc_obj -> socket_state & SocketStateTCPListening ) {
328
- (void ) tcp_close (rsrc_obj -> tcp_pcb );
329
- } else {
330
- tcp_abort (rsrc_obj -> tcp_pcb );
331
- }
332
- rsrc_obj -> tcp_pcb = NULL ;
333
- rsrc_obj -> socket_state = SocketStateClosed ;
334
- } else if (rsrc_obj -> socket_state & SocketStateUDP ) {
335
- udp_remove (rsrc_obj -> udp_pcb );
336
- rsrc_obj -> udp_pcb = NULL ;
337
- rsrc_obj -> socket_state = SocketStateClosed ;
327
+ LWIP_BEGIN ();
328
+ rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
329
+ if (rsrc_obj -> socket_state & SocketStateTCP ) {
330
+ if (rsrc_obj -> socket_state & SocketStateTCPListening ) {
331
+ (void ) tcp_close (rsrc_obj -> tcp_pcb );
332
+ } else {
333
+ tcp_abort (rsrc_obj -> tcp_pcb );
338
334
}
339
- LWIP_END ();
335
+ rsrc_obj -> tcp_pcb = NULL ;
336
+ rsrc_obj -> socket_state = SocketStateClosed ;
337
+ } else if (rsrc_obj -> socket_state & SocketStateUDP ) {
338
+ udp_remove (rsrc_obj -> udp_pcb );
339
+ rsrc_obj -> udp_pcb = NULL ;
340
+ rsrc_obj -> socket_state = SocketStateClosed ;
340
341
}
342
+ LWIP_END ();
341
343
#endif
342
344
343
345
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
346
+
347
+ // We're no longer monitoring so we can decrement ref count
344
348
refc_binary_decrement_refcount (rsrc_refc , caller_env -> global );
345
349
}
346
350
@@ -948,13 +952,16 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
948
952
RAISE_ERROR (BADARG_ATOM );
949
953
}
950
954
955
+ struct RefcBinary * rsrc_refc = refc_binary_from_data (rsrc_obj );
951
956
SMP_RWLOCK_WRLOCK (rsrc_obj -> socket_lock );
952
957
953
958
ErlNifEnv * env = erl_nif_env_from_context (ctx );
954
959
if (rsrc_obj -> selecting_process_id != ctx -> process_id && rsrc_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
955
960
// demonitor can fail if process is gone.
956
961
enif_demonitor_process (env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
957
962
rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
963
+ // decrement ref count as we are demonitoring
964
+ refc_binary_decrement_refcount (rsrc_refc , ctx -> global );
958
965
}
959
966
// Monitor first as select is less likely to fail and it's less expensive to demonitor
960
967
// if select fails than to stop select if monitor fails
@@ -963,6 +970,8 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
963
970
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
964
971
RAISE_ERROR (NOPROC_ATOM );
965
972
}
973
+ // increment ref count so the resource doesn't go away until monitor is fired
974
+ refc_binary_increment_refcount (rsrc_refc );
966
975
rsrc_obj -> selecting_process_id = ctx -> process_id ;
967
976
}
968
977
@@ -979,6 +988,7 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
979
988
enif_demonitor_process (env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
980
989
rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
981
990
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
991
+ refc_binary_decrement_refcount (rsrc_refc , ctx -> global );
982
992
RAISE_ERROR (BADARG_ATOM );
983
993
}
984
994
}
@@ -1025,6 +1035,7 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])
1025
1035
enif_demonitor_process (env , rsrc_obj , & rsrc_obj -> selecting_process_monitor );
1026
1036
LWIP_END ();
1027
1037
SMP_RWLOCK_UNLOCK (rsrc_obj -> socket_lock );
1038
+ refc_binary_decrement_refcount (rsrc_refc , ctx -> global );
1028
1039
RAISE_ERROR (BADARG_ATOM );
1029
1040
}
1030
1041
LWIP_END ();
@@ -1047,7 +1058,12 @@ static term nif_socket_select_stop(Context *ctx, int argc, term argv[])
1047
1058
}
1048
1059
// Avoid the race condition with select object here.
1049
1060
SMP_RWLOCK_WRLOCK (rsrc_obj -> socket_lock );
1050
- rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
1061
+ if (rsrc_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
1062
+ enif_demonitor_process (erl_nif_env_from_context (ctx ), rsrc_obj , & rsrc_obj -> selecting_process_monitor );
1063
+ rsrc_obj -> selecting_process_id = INVALID_PROCESS_ID ;
1064
+ struct RefcBinary * rsrc_refc = refc_binary_from_data (rsrc_obj );
1065
+ refc_binary_decrement_refcount (rsrc_refc , ctx -> global );
1066
+ }
1051
1067
#if OTP_SOCKET_BSD
1052
1068
if (UNLIKELY (enif_select (erl_nif_env_from_context (ctx ), rsrc_obj -> fd , ERL_NIF_SELECT_STOP , rsrc_obj , NULL , term_nil ()) < 0 )) {
1053
1069
RAISE_ERROR (BADARG_ATOM );
0 commit comments