@@ -275,60 +275,52 @@ fn send_ctrl_c_interrupts_process() {
275275 let cmd = CommandBuilder :: from ( command_for_fn ! ( ( ) , |( ) : ( ) | {
276276 use std:: io:: { Write , stdout} ;
277277
278- // On Unix, use signal_hook directly instead of ctrlc.
279- // ctrlc spawns a background thread to monitor signals, but the subprocess
280- // closure runs during .init_array (via ctor). On musl, newly-created threads
281- // cannot execute during init (musl holds a lock), so ctrlc's thread never
282- // runs and SIGINT is silently swallowed.
283- // signal_hook::low_level::register installs a raw signal handler with no
284- // background thread, avoiding the issue entirely.
285- #[ cfg( unix) ]
278+ // On Linux, use signalfd to wait for SIGINT without signal handlers or
279+ // background threads. This avoids musl issues where threads spawned during
280+ // .init_array (via ctor) are blocked by musl's internal lock.
281+ #[ cfg( target_os = "linux" ) ]
286282 {
287- use std :: sync :: {
288- Arc ,
289- atomic :: { AtomicBool , Ordering } ,
283+ use nix :: sys :: {
284+ signal :: { SigSet , Signal } ,
285+ signalfd :: SignalFd ,
290286 } ;
291287
292- let interrupted = Arc :: new( AtomicBool :: new( false ) ) ;
293- let flag = Arc :: clone( & interrupted) ;
288+ // Block SIGINT so it goes to signalfd instead of the default handler.
289+ let mut mask = SigSet :: empty( ) ;
290+ mask. add( Signal :: SIGINT ) ;
291+ mask. thread_block( ) . unwrap( ) ;
294292
295- // SAFETY: The closure only performs an atomic store, which is signal-safe.
296- unsafe {
297- signal_hook:: low_level:: register( signal_hook:: consts:: SIGINT , move || {
298- flag. store( true , Ordering :: SeqCst ) ;
299- } )
300- . unwrap( ) ;
301- }
293+ let sfd = SignalFd :: new( & mask) . unwrap( ) ;
302294
303295 println!( "ready" ) ;
304296 stdout( ) . flush( ) . unwrap( ) ;
305297
306- loop {
307- if interrupted. load( Ordering :: SeqCst ) {
308- print!( "INTERRUPTED" ) ;
309- stdout( ) . flush( ) . unwrap( ) ;
310- std:: process:: exit( 0 ) ;
311- }
312- std:: thread:: yield_now( ) ;
313- }
298+ // Block until SIGINT arrives via signalfd.
299+ sfd. read_signal( ) . unwrap( ) . unwrap( ) ;
300+ print!( "INTERRUPTED" ) ;
301+ stdout( ) . flush( ) . unwrap( ) ;
302+ std:: process:: exit( 0 ) ;
314303 }
315304
316- // On Windows, ctrlc works fine (no .init_array/musl issue).
317- #[ cfg( windows ) ]
305+ // On macOS/ Windows, use ctrlc which works fine (no .init_array/musl issue).
306+ #[ cfg( not ( target_os = "linux" ) ) ]
318307 {
319- // Clear the "ignore CTRL_C" flag set by Rust runtime
308+ // On Windows, clear the "ignore CTRL_C" flag set by Rust runtime
320309 // so that CTRL_C_EVENT reaches the ctrlc handler.
321- // SAFETY: Declaring correct signature for SetConsoleCtrlHandler from kernel32.
322- unsafe extern "system" {
323- fn SetConsoleCtrlHandler (
324- handler: Option <unsafe extern "system" fn ( u32 ) -> i32 >,
325- add: i32 ,
326- ) -> i32 ;
327- }
310+ #[ cfg( windows) ]
311+ {
312+ // SAFETY: Declaring correct signature for SetConsoleCtrlHandler from kernel32.
313+ unsafe extern "system" {
314+ fn SetConsoleCtrlHandler (
315+ handler: Option <unsafe extern "system" fn ( u32 ) -> i32 >,
316+ add: i32 ,
317+ ) -> i32 ;
318+ }
328319
329- // SAFETY: Clearing the "ignore CTRL_C" flag so handlers are invoked.
330- unsafe {
331- SetConsoleCtrlHandler ( None , 0 ) ; // FALSE = remove ignore
320+ // SAFETY: Clearing the "ignore CTRL_C" flag so handlers are invoked.
321+ unsafe {
322+ SetConsoleCtrlHandler ( None , 0 ) ; // FALSE = remove ignore
323+ }
332324 }
333325
334326 ctrlc:: set_handler( move || {
0 commit comments