(* TEST hassysthreads; include systhreads; not-target-windows; no-tsan; { bytecode; }{ native; } *) (* PR #11307. The following program deadlocks when input in the toplevel and interrupted by the user with Ctrl-C, by busy-waiting on signals to be processed. {[ let break_trap s = (try while true do () done with Sys.Break -> print_endline "[Sys.Break caught]" ) ; print_endline s let () = Sys.catch_break true ; let d = Domain.spawn (fun () -> break_trap "Domain 1") in break_trap "Domain 0 - 1" ; Domain.join d ; break_trap "Domain 0 - 2"; print_endline "Success." ]} *) let verbose = false (* Expected when verbose (depending on scheduling and platform): [Sys.Break caught] Domain 1 [Sys.Break caught] Domain 0 - 1 [Sys.Break caught] Domain 0 - 2 Success. *) let delay = 0.001 (* 1 ms *) let fuel = Atomic.make 1000 (* = 1s max retry duration *) let print = if verbose then print_endline else fun _ -> () (* start sending interrupts when reaches 1 or 2 *) let ready_count = Atomic.make 0 (* Does not poll *) let sleep () = if Atomic.get fuel <= 0 then ( print "[Reached max attempts without succeeding]"; Unix._exit 1 ); Atomic.decr fuel; Unix.sleepf delay let rec wait n = if Atomic.get ready_count <> n then ( sleep (); wait n ) (* We busy-wait because other synchronisation mechanisms involve blocking calls, which may exercise other parts of the async callback implementation than we want.*) let break_trap s = begin try Atomic.incr ready_count; while true do () done with Sys.Break -> print "[Sys.Break caught]" end; print s; Atomic.decr ready_count (* Simulate repeated Ctrl-C from a parallel thread *) let interruptor_domain () = Domain.spawn @@ fun () -> ignore (Thread.sigmask Unix.SIG_BLOCK [Sys.sigint]); let kill () = sleep () ; Unix.kill (Unix.getpid ()) Sys.sigint in wait 2; kill (); (* interrupt Domain 1 or Domain 0-1 *) wait 1; kill (); (* interrupt the other one of Domain 1 or Domain 0-1 *) wait 2; kill () (* interrupt Domain 0-2 *) let run () = (* We simulate the user pressing Ctrl-C repeatedly. Goal: joining the domain [d] must be achievable by Ctrl-C. This tests proper reception of SIGINT. *) let d = Domain.spawn (fun () -> break_trap "Domain 1") in let d2 = interruptor_domain () in break_trap "Domain 0 - 1"; Domain.join d; assert (Atomic.get ready_count = 0); Atomic.incr ready_count; (* Make sure it reaches 2 *) break_trap "Domain 0 - 2"; Domain.join d2 let () = Sys.catch_break true; (try run () with Sys.Break -> print ("Test could not complete due to scheduling hazard" ^ " (possible false positive).")); print "Success."; exit 0