Skip to content

Commit

Permalink
fix(tui): fix supsend/resume (#7763)
Browse files Browse the repository at this point in the history
Signed-off-by: Rudi Grinberg <[email protected]>
  • Loading branch information
rgrinberg authored Jun 17, 2023
1 parent 1542085 commit 59b44dc
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 17 deletions.
8 changes: 6 additions & 2 deletions src/dune_config_file/display.ml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ let console_backend = function
| Tui -> Dune_tui.backend ()
| Simple { status_line; _ } -> (
match status_line with
| false -> Dune_console.Backend.dumb
| false ->
Dune_util.Terminal_signals.unblock ();
Dune_console.Backend.dumb
| true -> (
match Config.(get threaded_console) with
| `Enabled -> Dune_threaded_console.progress ()
| `Disabled -> Dune_console.Backend.progress))
| `Disabled ->
Dune_util.Terminal_signals.unblock ();
Dune_console.Backend.progress))
3 changes: 2 additions & 1 deletion src/dune_engine/scheduler.ml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ let interrupt_signals : Signal.t list = [ Int; Quit; Term ]

(* In addition, the scheduler also blocks some other signals so that only
designated threads can handle them by unblocking *)
let blocked_signals : Signal.t list = Winch :: interrupt_signals
let blocked_signals : Signal.t list =
Dune_util.Terminal_signals.signals @ interrupt_signals

module Thread : sig
val spawn : signal_watcher:[ `Yes | `No ] -> (unit -> unit) -> unit
Expand Down
2 changes: 1 addition & 1 deletion src/dune_threaded_console/dune
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(library
(name dune_threaded_console)
(libraries stdune dune_console dune_engine threads.posix)
(libraries stdune dune_util dune_console dune_engine threads.posix)
(instrumentation
(backend bisect_ppx)))
3 changes: 1 addition & 2 deletions src/dune_threaded_console/dune_threaded_console.ml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ let make (module Base : S) : (module Dune_console.Backend) =
let start () =
Base.start ();
Dune_engine.Scheduler.spawn_thread @@ fun () ->
if not Sys.win32 then
ignore (Unix.sigprocmask SIG_UNBLOCK [ Signal.to_int Winch ] : int list);
Dune_util.Terminal_signals.unblock ();
let last = ref (Unix.gettimeofday ()) in
let frame_rate = 1. /. 60. in
let cleanup () =
Expand Down
55 changes: 44 additions & 11 deletions src/dune_tui/dune_tui.ml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,33 @@ let image_of_user_message_style_pp =
module Tui () = struct
module Term = Notty_unix.Term

let term = Term.create ~nosig:false ()
let nosig = false

let bytes = Bytes.make 64 '0'

let sigcont_r, sigcont_w = Unix.pipe ~cloexec:true ()

let term =
Unix.set_nonblock sigcont_r;
let term = ref (Term.create ~nosig ()) in
Sys.set_signal Sys.sigcont
@@ Sys.Signal_handle
(fun _ ->
Term.release !term;
term := Term.create ~nosig ();
assert (1 = Unix.single_write sigcont_w bytes 0 1));
let rec old =
lazy
(Sys.signal Sys.sigtstp
@@ Sys.Signal_handle
(fun i ->
Term.release !term;
match Lazy.force old with
| Sys.Signal_handle f -> f i
| _ -> Unix.kill (Unix.getpid ()) Sys.sigstop))
in
ignore (Lazy.force old);
fun () -> !term

let start () = Unix.set_nonblock Unix.stdin

Expand All @@ -126,37 +152,44 @@ module Tui () = struct
let render (state : Dune_threaded_console.state) =
let messages = Queue.to_list state.messages in
let image = image ~status_line:state.status_line ~messages in
Term.image term image
Term.image (term ()) image

let resize mutex (state : Dune_threaded_console.state) =
Mutex.lock mutex;
state.dirty <- true;
Mutex.unlock mutex
Mutex.unlock mutex;
Unix.gettimeofday ()

let rec handle_user_events ~now ~time_budget mutex state =
(* We check for any user input and handle it. If we go over the
[time_budget] we give up and continue. *)
let input_fds =
match Unix.select [ Unix.stdin ] [] [] time_budget with
match Unix.select [ Unix.stdin; sigcont_r ] [] [] time_budget with
| exception Unix.Unix_error (EINTR, _, _) -> `Restore
| [], _, _ -> `Timeout
| _ :: _, _, _ -> `Event
| exception Unix.Unix_error (EINTR, _, _) -> `Event
| fds, _, _ -> (
match List.exists fds ~f:(Poly.equal sigcont_r) with
| false -> `Event
| true ->
ignore (Unix.read sigcont_r bytes 0 1);
(* backgrounding could have changed the cursor settings for example.
we need to restore all this stuff *)
`Restore)
in
match input_fds with
| `Restore -> resize mutex state
| `Timeout ->
now +. time_budget
(* Nothing to do, we return the time at the end of the time budget. *)
| `Event -> (
(* TODO if anything fancy is done in the UI in the future we need to lock
the state with the provided mutex *)
match Term.event term with
match Term.event (term ()) with
| `Key (`ASCII 'q', _) ->
(* When we encounter q we make sure to quit by signaling termination. *)
Unix.kill (Unix.getpid ()) Sys.sigterm;
Unix.gettimeofday ()
| `Resize _ ->
resize mutex state;
Unix.gettimeofday ()
| `Resize _ -> resize mutex state
| _ -> Unix.gettimeofday ()
| exception Unix.Unix_error ((EAGAIN | EWOULDBLOCK), _, _) ->
(* If we encounter an exception, we make sure to rehandle user events
Expand All @@ -172,7 +205,7 @@ module Tui () = struct
let reset_flush_history () = ()

let finish () =
Notty_unix.Term.release term;
Term.release (term ());
Unix.clear_nonblock Unix.stdin
end

Expand Down
1 change: 1 addition & 0 deletions src/dune_util/dune_util.ml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module Flock = Flock
module Global_lock = Global_lock
module Action = Action
module Alias_name = Alias_name
module Terminal_signals = Terminal_signals
open Stdune

let xdg =
Expand Down
13 changes: 13 additions & 0 deletions src/dune_util/terminal_signals.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
open Stdune

let signals : Signal.t list =
[ Cont (* restore the terminal after process is resumed *)
; Tstp (* to restore the terminal after it's stopped (C-z) *)
; Winch (* Re-render thet terminal when the terminal is resized *)
]

let unblock () =
if not Sys.win32 then
ignore
(Unix.sigprocmask SIG_UNBLOCK (List.map ~f:Signal.to_int signals)
: int list)
7 changes: 7 additions & 0 deletions src/dune_util/terminal_signals.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open Stdune

(** The signals that must be handled in the thread that's handling terminal ui
events. *)
val signals : Signal.t list

val unblock : unit -> unit

0 comments on commit 59b44dc

Please sign in to comment.