mirror of
https://github.com/wez/wezterm.git
synced 2024-11-26 16:34:23 +03:00
fix lingering after closing down
The root cause of this was a bit of a hack to ensure that we didn't prematurely shut down while waiting for ssh sessions. Introduce an Activity token that will extend the lifetime of the event loop even if there are no windows present. This cleans things up both on macos the application would linger in the application switcher until you had tabbed away and back again, and also for the null frontend which had grown a less gross hack.
This commit is contained in:
parent
9670fc3fdf
commit
93256f3339
29
src/frontend/activity.rs
Normal file
29
src/frontend/activity.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//! Keeps track of the number of user-initiated activities
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// Create and hold on to an Activity while you are processing
|
||||
/// the direct result of a user initiated action, such as preparing
|
||||
/// to open a window.
|
||||
/// Once you have opened the window, drop the activity.
|
||||
/// The activity is used to keep the frontend alive even if there
|
||||
/// may be no windows present in the mux.
|
||||
pub struct Activity {}
|
||||
|
||||
impl Activity {
|
||||
pub fn new() -> Self {
|
||||
COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn count() -> usize {
|
||||
COUNT.load(Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Activity {
|
||||
fn drop(&mut self) {
|
||||
COUNT.fetch_sub(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
@ -87,40 +87,13 @@ impl FrontEnd for GuiFrontEnd {
|
||||
}
|
||||
|
||||
fn run_forever(&self) -> anyhow::Result<()> {
|
||||
// We run until we've run out of windows in the Mux.
|
||||
// When we're running ssh we have a transient window
|
||||
// or two during authentication and we want to de-bounce
|
||||
// our decision to quit until we're sure that we have
|
||||
// no windows, so we track it here.
|
||||
struct State {
|
||||
when: Option<Instant>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn mark(&mut self, is_empty: bool) {
|
||||
if is_empty {
|
||||
let now = Instant::now();
|
||||
if let Some(start) = self.when.as_ref() {
|
||||
let diff = now - *start;
|
||||
if diff > Duration::new(5, 0) {
|
||||
Connection::get().unwrap().terminate_message_loop();
|
||||
}
|
||||
} else {
|
||||
self.when = Some(now);
|
||||
}
|
||||
} else {
|
||||
self.when = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state = Arc::new(Mutex::new(State { when: None }));
|
||||
|
||||
self.connection
|
||||
.schedule_timer(std::time::Duration::from_millis(200), move || {
|
||||
let mux = Mux::get().unwrap();
|
||||
mux.prune_dead_windows();
|
||||
state.lock().unwrap().mark(mux.is_empty());
|
||||
if mux.is_empty() && crate::frontend::activity::Activity::count() == 0 {
|
||||
Connection::get().unwrap().terminate_message_loop();
|
||||
}
|
||||
});
|
||||
|
||||
self.connection.run_message_loop()
|
||||
|
@ -10,6 +10,7 @@ use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub mod activity;
|
||||
pub mod gui;
|
||||
pub mod muxserver;
|
||||
|
||||
|
@ -33,9 +33,6 @@ impl Executor for MuxExecutor {
|
||||
pub struct MuxServerFrontEnd {
|
||||
tx: Sender<SpawnFunc>,
|
||||
rx: Receiver<SpawnFunc>,
|
||||
/// Indicates if we're the null frontend or not.
|
||||
/// If so, our termination behavior is altered.
|
||||
is_null: bool,
|
||||
}
|
||||
|
||||
impl MuxServerFrontEnd {
|
||||
@ -46,8 +43,7 @@ impl MuxServerFrontEnd {
|
||||
if start_listener {
|
||||
spawn_listener()?;
|
||||
}
|
||||
let is_null = !start_listener;
|
||||
Ok(Rc::new(Self { tx, rx, is_null }))
|
||||
Ok(Rc::new(Self { tx, rx }))
|
||||
}
|
||||
|
||||
pub fn try_new() -> Result<Rc<dyn FrontEnd>, Error> {
|
||||
@ -77,7 +73,7 @@ impl FrontEnd for MuxServerFrontEnd {
|
||||
Err(err) => bail!("while waiting for events: {:?}", err),
|
||||
}
|
||||
|
||||
if !self.is_null && Mux::get().unwrap().is_empty() {
|
||||
if Mux::get().unwrap().is_empty() && crate::frontend::activity::Activity::count() == 0 {
|
||||
info!("No more tabs; all done!");
|
||||
return Ok(());
|
||||
}
|
||||
|
23
src/main.rs
23
src/main.rs
@ -27,6 +27,7 @@ mod ssh;
|
||||
mod stats;
|
||||
mod termwiztermtab;
|
||||
|
||||
use crate::frontend::activity::Activity;
|
||||
use crate::frontend::{executor, front_end, FrontEndSelection};
|
||||
use crate::mux::domain::{Domain, LocalDomain};
|
||||
use crate::mux::Mux;
|
||||
@ -361,6 +362,10 @@ fn run_ssh(config: config::ConfigHandle, opts: &SshCommand) -> anyhow::Result<()
|
||||
let mux = Rc::new(mux::Mux::new(None));
|
||||
Mux::set_mux(&mux);
|
||||
|
||||
// Keep the frontend alive until we've run through the ssh authentication
|
||||
// phase. This is passed into the thread and dropped when it is done.
|
||||
let activity = Activity::new();
|
||||
|
||||
// Initiate an ssh connection; since that is a blocking process with
|
||||
// callbacks, we have to run it in another thread
|
||||
std::thread::spawn(move || {
|
||||
@ -406,6 +411,11 @@ fn run_ssh(config: config::ConfigHandle, opts: &SshCommand) -> anyhow::Result<()
|
||||
let window_id = mux.new_empty_window();
|
||||
let tab = domain.spawn(PtySize::default(), cmd, window_id)?;
|
||||
gui.spawn_new_window(&fontconfig, &tab, window_id)?;
|
||||
|
||||
// This captures the activity ownership into this future, but also
|
||||
// ensures that we drop it either when we error out, or if not,
|
||||
// only once we reach this point in the processing flow.
|
||||
drop(activity);
|
||||
Ok(())
|
||||
});
|
||||
});
|
||||
@ -772,6 +782,10 @@ fn run() -> anyhow::Result<()> {
|
||||
let sock_path = unix_dom.socket_path();
|
||||
let stream = unix_connect_with_retry(&sock_path)?;
|
||||
|
||||
// Keep the threads below alive forever; they'll
|
||||
// exit the process when they're done.
|
||||
let activity = Activity::new();
|
||||
|
||||
// Spawn a thread to pull data from the socket and write
|
||||
// it to stdout
|
||||
let duped = stream.try_clone()?;
|
||||
@ -785,15 +799,6 @@ fn run() -> anyhow::Result<()> {
|
||||
let stdin = std::io::stdin();
|
||||
consume_stream_then_exit_process(stdin.lock(), stream);
|
||||
});
|
||||
// This is a bit gross; we run a Null frontend while we
|
||||
// manage our streams to avoid a panic with some code
|
||||
// that spawns some background processing via the executors.
|
||||
// However, since we don't register any domains, and the
|
||||
// threads we spawn above to consume the streams are not
|
||||
// associated with the mux layer, this call to run_forever
|
||||
// really will run forever.
|
||||
// For that reason, we've arranged for the threads above
|
||||
// to exit the process if they run out of data to consume.
|
||||
front_end.run_forever()?;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user