3535
3636#include "defaultatoms.h"
3737#include "erl_nif_priv.h"
38+ #include "globalcontext.h"
3839#include "interop.h"
3940#include "nifs.h"
4041#include "posix_nifs.h"
@@ -115,7 +116,8 @@ term posix_errno_to_term(int err, GlobalContext *glb)
115116struct PosixFd
116117{
117118 int fd ;
118- bool select ;
119+ int32_t selecting_process_id ;
120+ ErlNifMonitor selecting_process_monitor ;
119121};
120122
121123static void posix_fd_dtor (ErlNifEnv * caller_env , void * obj )
@@ -131,18 +133,32 @@ static void posix_fd_dtor(ErlNifEnv *caller_env, void *obj)
131133
132134static void posix_fd_stop (ErlNifEnv * caller_env , void * obj , ErlNifEvent event , int is_direct_call )
133135{
134- UNUSED (caller_env );
135136 UNUSED (event );
136137 UNUSED (is_direct_call );
137138
138139 struct PosixFd * fd_obj = (struct PosixFd * ) obj ;
139- fd_obj -> select = false;
140+ if (fd_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
141+ enif_demonitor_process (caller_env , fd_obj , & fd_obj -> selecting_process_monitor );
142+ fd_obj -> selecting_process_id = INVALID_PROCESS_ID ;
143+ }
144+ }
145+
146+ static void posix_fd_down (ErlNifEnv * caller_env , void * obj , ErlNifPid * pid , ErlNifMonitor * mon )
147+ {
148+ UNUSED (pid );
149+ UNUSED (mon );
150+ struct PosixFd * fd_obj = (struct PosixFd * ) obj ;
151+ if (fd_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
152+ fd_obj -> selecting_process_id = INVALID_PROCESS_ID ;
153+ enif_select (caller_env , fd_obj -> fd , ERL_NIF_SELECT_STOP , fd_obj , NULL , term_nil ());
154+ }
140155}
141156
142157const ErlNifResourceTypeInit posix_fd_resource_type_init = {
143- .members = 2 ,
158+ .members = 3 ,
144159 .dtor = posix_fd_dtor ,
145160 .stop = posix_fd_stop ,
161+ .down = posix_fd_down ,
146162};
147163
148164#define O_EXEC_ATOM_STR ATOM_STR("\x6", "o_exec")
@@ -270,7 +286,7 @@ static term nif_atomvm_posix_open(Context *ctx, int argc, term argv[])
270286 RAISE_ERROR (OUT_OF_MEMORY_ATOM );
271287 }
272288 fd_obj -> fd = fd ;
273- fd_obj -> select = false ;
289+ fd_obj -> selecting_process_id = INVALID_PROCESS_ID ;
274290 if (UNLIKELY (memory_ensure_free_opt (ctx , TUPLE_SIZE (2 ) + TERM_BOXED_RESOURCE_SIZE , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
275291 RAISE_ERROR (OUT_OF_MEMORY_ATOM );
276292 }
@@ -295,7 +311,7 @@ static term nif_atomvm_posix_close(Context *ctx, int argc, term argv[])
295311 }
296312 struct PosixFd * fd_obj = (struct PosixFd * ) fd_obj_ptr ;
297313 if (fd_obj -> fd != CLOSED_FD ) {
298- if (fd_obj -> select ) {
314+ if (fd_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
299315 fprintf (stderr , "Calling close on a selectable posix file, missing call to posix_select_stop?\n" );
300316 }
301317 if (UNLIKELY (close (fd_obj -> fd ) < 0 )) {
@@ -398,11 +414,26 @@ static term nif_atomvm_posix_select(Context *ctx, term argv[], enum ErlNifSelect
398414 RAISE_ERROR (BADARG_ATOM );
399415 }
400416 struct PosixFd * fd_obj = (struct PosixFd * ) fd_obj_ptr ;
401-
402- if (UNLIKELY (enif_select (erl_nif_env_from_context (ctx ), fd_obj -> fd , mode , fd_obj , & process_pid , select_ref_term ) < 0 )) {
417+ ErlNifEnv * env = erl_nif_env_from_context (ctx );
418+ if (fd_obj -> selecting_process_id != process_pid && fd_obj -> selecting_process_id != INVALID_PROCESS_ID ) {
419+ if (UNLIKELY (enif_demonitor_process (env , fd_obj , & fd_obj -> selecting_process_monitor ) != 0 )) {
420+ RAISE_ERROR (BADARG_ATOM );
421+ }
422+ fd_obj -> selecting_process_id = INVALID_PROCESS_ID ;
423+ }
424+ // Monitor first as select is less likely to fail and it's less expensive to demonitor
425+ // if select fails than to stop select if monitor fails
426+ if (fd_obj -> selecting_process_id != process_pid ) {
427+ if (UNLIKELY (enif_monitor_process (env , fd_obj , & process_pid , & fd_obj -> selecting_process_monitor ) != 0 )) {
428+ RAISE_ERROR (BADARG_ATOM );
429+ }
430+ fd_obj -> selecting_process_id = process_pid ;
431+ }
432+ if (UNLIKELY (enif_select (env , fd_obj -> fd , mode , fd_obj , & process_pid , select_ref_term ) < 0 )) {
433+ enif_demonitor_process (env , fd_obj , & fd_obj -> selecting_process_monitor );
434+ fd_obj -> selecting_process_id = INVALID_PROCESS_ID ;
403435 RAISE_ERROR (BADARG_ATOM );
404436 }
405- fd_obj -> select = 1 ;
406437
407438 return OK_ATOM ;
408439}
0 commit comments