1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-29 00:21:57 +03:00

mux: Domain::spawn is now async

This was another source of hanging on windows with connecting to a unix
domain.
This commit is contained in:
Wez Furlong 2020-01-16 16:36:13 -08:00
parent 7de282fd07
commit e814bc4267
7 changed files with 114 additions and 98 deletions

View File

@ -1048,61 +1048,66 @@ impl TermWindow {
self.move_tab(tab)
}
fn spawn_tab(&mut self, domain: &SpawnTabDomain) -> anyhow::Result<TabId> {
fn spawn_tab(&mut self, domain: &SpawnTabDomain) {
let size = self.terminal_size;
let mux = Mux::get().unwrap();
let (domain, cwd) = match domain {
SpawnTabDomain::DefaultDomain => {
let cwd = mux
.get_active_tab_for_window(self.mux_window_id)
.and_then(|tab| tab.get_current_working_dir());
(mux.default_domain().clone(), cwd)
}
SpawnTabDomain::CurrentTabDomain => {
let tab = match mux.get_active_tab_for_window(self.mux_window_id) {
Some(tab) => tab,
None => bail!("window has no tabs?"),
};
(
mux.get_domain(tab.domain_id())
.ok_or_else(|| anyhow!("current tab has unresolvable domain id!?"))?,
tab.get_current_working_dir(),
)
}
SpawnTabDomain::Domain(id) => (
mux.get_domain(*id)
.ok_or_else(|| anyhow!("spawn_tab called with unresolvable domain id!?"))?,
None,
),
SpawnTabDomain::DomainName(name) => (
mux.get_domain_by_name(&name).ok_or_else(|| {
anyhow!("spawn_tab called with unresolvable domain name {}", name)
})?,
None,
),
};
let cwd = match cwd {
Some(url) if url.scheme() == "file" => Some(url.path().to_string()),
Some(_) | None => None,
};
let tab = domain.spawn(size, None, cwd, self.mux_window_id)?;
let tab_id = tab.tab_id();
let mux_window_id = self.mux_window_id;
let clipboard: Arc<dyn term::Clipboard> = Arc::new(ClipboardHelper {
window: self.window.as_ref().unwrap().clone(),
clipboard_contents: Arc::clone(&self.clipboard_contents),
});
tab.set_clipboard(&clipboard);
let domain = domain.clone();
let len = {
let window = mux
.get_window(self.mux_window_id)
promise::spawn::spawn(async move {
let mux = Mux::get().unwrap();
let (domain, cwd) = match domain {
SpawnTabDomain::DefaultDomain => {
let cwd = mux
.get_active_tab_for_window(mux_window_id)
.and_then(|tab| tab.get_current_working_dir());
(mux.default_domain().clone(), cwd)
}
SpawnTabDomain::CurrentTabDomain => {
let tab = match mux.get_active_tab_for_window(mux_window_id) {
Some(tab) => tab,
None => bail!("window has no tabs?"),
};
(
mux.get_domain(tab.domain_id())
.ok_or_else(|| anyhow!("current tab has unresolvable domain id!?"))?,
tab.get_current_working_dir(),
)
}
SpawnTabDomain::Domain(id) => (
mux.get_domain(id)
.ok_or_else(|| anyhow!("spawn_tab called with unresolvable domain id!?"))?,
None,
),
SpawnTabDomain::DomainName(name) => (
mux.get_domain_by_name(&name).ok_or_else(|| {
anyhow!("spawn_tab called with unresolvable domain name {}", name)
})?,
None,
),
};
let cwd = match cwd {
Some(url) if url.scheme() == "file" => Some(url.path().to_string()),
Some(_) | None => None,
};
let tab = domain.spawn(size, None, cwd, mux_window_id).await?;
let tab_id = tab.tab_id();
tab.set_clipboard(&clipboard);
let mut window = mux
.get_window_mut(mux_window_id)
.ok_or_else(|| anyhow!("no such window!?"))?;
window.len()
};
self.activate_tab(len - 1)?;
Ok(tab_id)
if let Some(idx) = window.idx_by_id(tab_id) {
window.set_active(idx);
}
Ok(())
});
}
fn selection_text(&self, tab: &Rc<dyn Tab>) -> String {
@ -1141,7 +1146,7 @@ impl TermWindow {
use KeyAssignment::*;
match assignment {
SpawnTab(spawn_where) => {
self.spawn_tab(spawn_where)?;
self.spawn_tab(spawn_where);
}
SpawnWindow => {
self.spawn_new_window();
@ -1204,10 +1209,11 @@ impl TermWindow {
let window_id = mux.new_empty_window();
let tab = mux
.default_domain()
.spawn(PtySize::default(), None, None, window_id)?;
.spawn(PtySize::default(), None, None, window_id)
.await?;
let front_end = front_end().expect("to be called on gui thread");
front_end.spawn_new_window(&fonts, &tab, window_id)?;
Ok(())
Ok::<(), anyhow::Error>(())
}
promise::spawn::spawn(async move {
new_window().await.ok();
@ -2355,7 +2361,7 @@ impl TermWindow {
self.activate_tab(tab_idx).ok();
}
TabBarItem::NewTabButton => {
self.spawn_tab(&SpawnTabDomain::CurrentTabDomain).ok();
self.spawn_tab(&SpawnTabDomain::CurrentTabDomain);
}
TabBarItem::None => {}
}

View File

@ -2,6 +2,7 @@
#![windows_subsystem = "windows"]
use anyhow::{anyhow, bail, Context};
use promise::spawn::block_on;
use std::ffi::OsString;
use std::fs::DirBuilder;
use std::io::{Read, Write};
@ -373,7 +374,9 @@ async fn async_run_ssh(opts: SshCommand, params: SshParameters) -> anyhow::Resul
domain.attach().await?;
let window_id = mux.new_empty_window();
let tab = domain.spawn(PtySize::default(), cmd, None, window_id)?;
let tab = domain
.spawn(PtySize::default(), cmd, None, window_id)
.await?;
let fontconfig = Rc::new(FontConfiguration::new());
gui.spawn_new_window(&fontconfig, &tab, window_id)?;
@ -426,10 +429,10 @@ fn run_serial(config: config::ConfigHandle, opts: &SerialCommand) -> anyhow::Res
let front_end = opts.front_end.unwrap_or(config.front_end);
let gui = front_end.try_new()?;
promise::spawn::block_on(domain.attach())?; // FIXME: blocking
block_on(domain.attach())?; // FIXME: blocking
let window_id = mux.new_empty_window();
let tab = domain.spawn(PtySize::default(), None, None, window_id)?;
let tab = block_on(domain.spawn(PtySize::default(), None, None, window_id))?; // FIXME: blocking
gui.spawn_new_window(&fontconfig, &tab, window_id)?;
gui.run_forever()
@ -506,7 +509,8 @@ async fn spawn_tab_in_default_domain_if_mux_is_empty(
let tab = mux
.default_domain()
.spawn(PtySize::default(), cmd, None, window_id)?;
.spawn(PtySize::default(), cmd, None, window_id)
.await?;
let fontconfig = Rc::new(FontConfiguration::new());
front_end()
.unwrap()

View File

@ -34,7 +34,7 @@ pub fn alloc_domain_id() -> DomainId {
#[async_trait(?Send)]
pub trait Domain: Downcast {
/// Spawn a new command within this domain
fn spawn(
async fn spawn(
&self,
size: PtySize,
command: Option<CommandBuilder>,
@ -84,7 +84,7 @@ impl LocalDomain {
#[async_trait(?Send)]
impl Domain for LocalDomain {
fn spawn(
async fn spawn(
&self,
size: PtySize,
command: Option<CommandBuilder>,

View File

@ -214,7 +214,7 @@ impl Domain for ClientDomain {
self.config.name()
}
fn spawn(
async fn spawn(
&self,
size: PtySize,
command: Option<CommandBuilder>,
@ -234,7 +234,7 @@ impl Domain for ClientDomain {
command,
command_dir,
})
.wait()?;
.await?;
inner.record_remote_to_local_window_mapping(result.window_id, window);

View File

@ -788,42 +788,7 @@ impl<S: ReadAndWrite> ClientSession<S> {
Pdu::Spawn(spawn) => {
let sender = self.to_write_tx.clone();
spawn_into_main_thread(async move {
catch(
move || {
let mux = Mux::get().unwrap();
let domain = mux.get_domain(spawn.domain_id).ok_or_else(|| {
anyhow!("domain {} not found on this server", spawn.domain_id)
})?;
let window_id = if let Some(window_id) = spawn.window_id {
mux.get_window_mut(window_id).ok_or_else(|| {
anyhow!("window_id {} not found on this server", window_id)
})?;
window_id
} else {
mux.new_empty_window()
};
let tab = domain.spawn(
spawn.size,
spawn.command,
spawn.command_dir,
window_id,
)?;
let clip: Arc<dyn Clipboard> = Arc::new(RemoteClipboard {
tab_id: tab.tab_id(),
sender,
});
tab.set_clipboard(&clip);
Ok(Pdu::SpawnResponse(SpawnResponse {
tab_id: tab.tab_id(),
window_id,
}))
},
send_response,
)
schedule_domain_spawn(spawn, sender, send_response);
});
}
@ -900,6 +865,47 @@ impl<S: ReadAndWrite> ClientSession<S> {
}
}
// Dancing around a little bit here; we can't directly spawn_into_main_thread the domain_spawn
// function below because the compiler thinks that all of its locals then need to be Send.
// We need to shimmy through this helper to break that aspect of the compiler flow
// analysis and allow things to compile.
fn schedule_domain_spawn<SND>(spawn: Spawn, sender: PollableSender<DecodedPdu>, send_response: SND)
where
SND: Fn(anyhow::Result<Pdu>) + 'static,
{
promise::spawn::spawn(async move { send_response(domain_spawn(spawn, sender).await) });
}
async fn domain_spawn(spawn: Spawn, sender: PollableSender<DecodedPdu>) -> anyhow::Result<Pdu> {
let mux = Mux::get().unwrap();
let domain = mux
.get_domain(spawn.domain_id)
.ok_or_else(|| anyhow!("domain {} not found on this server", spawn.domain_id))?;
let window_id = if let Some(window_id) = spawn.window_id {
mux.get_window_mut(window_id)
.ok_or_else(|| anyhow!("window_id {} not found on this server", window_id))?;
window_id
} else {
mux.new_empty_window()
};
let tab = domain
.spawn(spawn.size, spawn.command, spawn.command_dir, window_id)
.await?;
let clip: Arc<dyn Clipboard> = Arc::new(RemoteClipboard {
tab_id: tab.tab_id(),
sender,
});
tab.set_clipboard(&clip);
Ok::<Pdu, anyhow::Error>(Pdu::SpawnResponse(SpawnResponse {
tab_id: tab.tab_id(),
window_id,
}))
}
/// Unfortunately, novice unix users can sometimes be running
/// with an overly permissive umask so we take care to install
/// a more restrictive mask while we might be creating things

View File

@ -332,7 +332,7 @@ impl RemoteSshDomain {
#[async_trait(?Send)]
impl Domain for RemoteSshDomain {
fn spawn(
async fn spawn(
&self,
size: PtySize,
command: Option<CommandBuilder>,

View File

@ -149,7 +149,7 @@ impl TermWizTerminalDomain {
#[async_trait(?Send)]
impl Domain for TermWizTerminalDomain {
fn spawn(
async fn spawn(
&self,
_size: PtySize,
_command: Option<CommandBuilder>,