@@ -1724,6 +1724,10 @@ static ERL_NIF_TERM nif_set_callback_handler(ErlNifEnv *env, int argc, const ERL
17241724 * dirty scheduler forever (the pipe write ends are set non-blocking). */
17251725#define CALLBACK_RESPONSE_IO_TIMEOUT_MS 30000
17261726
1727+ /* Bound for OWN_GIL dispatch pipe I/O so a stalled/dead worker thread can't
1728+ * block the dispatching dirty scheduler forever. */
1729+ #define OWNGIL_IO_TIMEOUT_MS 30000
1730+
17271731static ERL_NIF_TERM nif_send_callback_response (ErlNifEnv * env , int argc , const ERL_NIF_TERM argv []) {
17281732 (void )argc ;
17291733 int fd ;
@@ -7292,16 +7296,19 @@ static ERL_NIF_TERM nif_owngil_create_session(ErlNifEnv *env, int argc,
72927296 .payload_len = 0 ,
72937297 };
72947298
7295- /* Write header */
7296- if (write (w -> cmd_pipe [1 ], & header , sizeof (header )) != sizeof (header )) {
7299+ /* Write header (non-blocking write end + deadline so a stalled/dead worker
7300+ * can't block this dirty scheduler forever). */
7301+ if (write_all_with_deadline (w -> cmd_pipe [1 ], & header , sizeof (header ),
7302+ OWNGIL_IO_TIMEOUT_MS ) != WRITE_OK ) {
72977303 pthread_mutex_unlock (& w -> dispatch_mutex );
72987304 return enif_make_tuple2 (env , ATOM_ERROR ,
72997305 enif_make_atom (env , "write_failed" ));
73007306 }
73017307
7302- /* Wait for response */
7308+ /* Wait for response, bounded by a deadline. */
73037309 owngil_header_t resp ;
7304- if (read (w -> result_pipe [0 ], & resp , sizeof (resp )) != sizeof (resp )) {
7310+ if (read_with_timeout (w -> result_pipe [0 ], & resp , sizeof (resp ),
7311+ OWNGIL_IO_TIMEOUT_MS ) != (ssize_t )sizeof (resp )) {
73057312 pthread_mutex_unlock (& w -> dispatch_mutex );
73067313 return enif_make_tuple2 (env , ATOM_ERROR ,
73077314 enif_make_atom (env , "read_failed" ));
@@ -7385,9 +7392,11 @@ static ERL_NIF_TERM nif_owngil_submit_task(ErlNifEnv *env, int argc,
73857392 .payload_len = payload_bin .size ,
73867393 };
73877394
7388- /* Write header and payload */
7389- if (write (w -> cmd_pipe [1 ], & header , sizeof (header )) != sizeof (header ) ||
7390- write (w -> cmd_pipe [1 ], payload_bin .data , payload_bin .size ) != (ssize_t )payload_bin .size ) {
7395+ /* Write header and payload (non-blocking write end + deadline). */
7396+ if (write_all_with_deadline (w -> cmd_pipe [1 ], & header , sizeof (header ),
7397+ OWNGIL_IO_TIMEOUT_MS ) != WRITE_OK ||
7398+ write_all_with_deadline (w -> cmd_pipe [1 ], payload_bin .data , payload_bin .size ,
7399+ OWNGIL_IO_TIMEOUT_MS ) != WRITE_OK ) {
73917400 pthread_mutex_unlock (& w -> dispatch_mutex );
73927401 enif_release_binary (& payload_bin );
73937402 return enif_make_tuple2 (env , ATOM_ERROR ,
@@ -7443,11 +7452,13 @@ static ERL_NIF_TERM nif_owngil_destroy_session(ErlNifEnv *env, int argc,
74437452 .payload_len = 0 ,
74447453 };
74457454
7446- /* Write header */
7447- if (write (w -> cmd_pipe [1 ], & header , sizeof (header )) == sizeof (header )) {
7448- /* Wait for response */
7455+ /* Write header (best-effort, bounded). */
7456+ if (write_all_with_deadline (w -> cmd_pipe [1 ], & header , sizeof (header ),
7457+ OWNGIL_IO_TIMEOUT_MS ) == WRITE_OK ) {
7458+ /* Wait for response (best-effort, bounded). */
74497459 owngil_header_t resp ;
7450- read (w -> result_pipe [0 ], & resp , sizeof (resp ));
7460+ (void )read_with_timeout (w -> result_pipe [0 ], & resp , sizeof (resp ),
7461+ OWNGIL_IO_TIMEOUT_MS );
74517462 }
74527463
74537464 pthread_mutex_unlock (& w -> dispatch_mutex );
@@ -7504,12 +7515,15 @@ static ERL_NIF_TERM nif_owngil_apply_imports(ErlNifEnv *env, int argc,
75047515 .payload_len = payload_bin .size ,
75057516 };
75067517
7507- /* Write header and payload */
7508- if (write (w -> cmd_pipe [1 ], & header , sizeof (header )) == sizeof (header )) {
7509- write (w -> cmd_pipe [1 ], payload_bin .data , payload_bin .size );
7510- /* Wait for response */
7518+ /* Write header and payload (best-effort, bounded). */
7519+ if (write_all_with_deadline (w -> cmd_pipe [1 ], & header , sizeof (header ),
7520+ OWNGIL_IO_TIMEOUT_MS ) == WRITE_OK ) {
7521+ (void )write_all_with_deadline (w -> cmd_pipe [1 ], payload_bin .data , payload_bin .size ,
7522+ OWNGIL_IO_TIMEOUT_MS );
7523+ /* Wait for response (best-effort, bounded). */
75117524 owngil_header_t resp ;
7512- read (w -> result_pipe [0 ], & resp , sizeof (resp ));
7525+ (void )read_with_timeout (w -> result_pipe [0 ], & resp , sizeof (resp ),
7526+ OWNGIL_IO_TIMEOUT_MS );
75137527 }
75147528
75157529 enif_release_binary (& payload_bin );
@@ -7567,12 +7581,15 @@ static ERL_NIF_TERM nif_owngil_apply_paths(ErlNifEnv *env, int argc,
75677581 .payload_len = payload_bin .size ,
75687582 };
75697583
7570- /* Write header and payload */
7571- if (write (w -> cmd_pipe [1 ], & header , sizeof (header )) == sizeof (header )) {
7572- write (w -> cmd_pipe [1 ], payload_bin .data , payload_bin .size );
7573- /* Wait for response */
7584+ /* Write header and payload (best-effort, bounded). */
7585+ if (write_all_with_deadline (w -> cmd_pipe [1 ], & header , sizeof (header ),
7586+ OWNGIL_IO_TIMEOUT_MS ) == WRITE_OK ) {
7587+ (void )write_all_with_deadline (w -> cmd_pipe [1 ], payload_bin .data , payload_bin .size ,
7588+ OWNGIL_IO_TIMEOUT_MS );
7589+ /* Wait for response (best-effort, bounded). */
75747590 owngil_header_t resp ;
7575- read (w -> result_pipe [0 ], & resp , sizeof (resp ));
7591+ (void )read_with_timeout (w -> result_pipe [0 ], & resp , sizeof (resp ),
7592+ OWNGIL_IO_TIMEOUT_MS );
75767593 }
75777594
75787595 enif_release_binary (& payload_bin );
@@ -7866,11 +7883,11 @@ static ErlNifFunc nif_funcs[] = {
78667883 {"subinterp_thread_pool_stats" , 0 , nif_subinterp_thread_pool_stats , 0 },
78677884
78687885 /* OWN_GIL session management for event loop pool */
7869- {"owngil_create_session" , 1 , nif_owngil_create_session , 0 },
7870- {"owngil_submit_task" , 7 , nif_owngil_submit_task , 0 },
7871- {"owngil_destroy_session" , 2 , nif_owngil_destroy_session , 0 },
7872- {"owngil_apply_imports" , 3 , nif_owngil_apply_imports , 0 },
7873- {"owngil_apply_paths" , 3 , nif_owngil_apply_paths , 0 },
7886+ {"owngil_create_session" , 1 , nif_owngil_create_session , ERL_NIF_DIRTY_JOB_IO_BOUND },
7887+ {"owngil_submit_task" , 7 , nif_owngil_submit_task , ERL_NIF_DIRTY_JOB_IO_BOUND },
7888+ {"owngil_destroy_session" , 2 , nif_owngil_destroy_session , ERL_NIF_DIRTY_JOB_IO_BOUND },
7889+ {"owngil_apply_imports" , 3 , nif_owngil_apply_imports , ERL_NIF_DIRTY_JOB_IO_BOUND },
7890+ {"owngil_apply_paths" , 3 , nif_owngil_apply_paths , ERL_NIF_DIRTY_JOB_IO_BOUND },
78747891
78757892 /* Execution mode info */
78767893 {"execution_mode" , 0 , nif_execution_mode , 0 },
0 commit comments