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

pty: async example using smol

refs: https://github.com/wez/wezterm/issues/166
This commit is contained in:
Wez Furlong 2020-05-09 10:23:15 -07:00
parent e2d044bb14
commit 2ef985aa18
8 changed files with 251 additions and 17 deletions

188
Cargo.lock generated
View File

@ -134,7 +134,7 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267"
dependencies = [
"async-task",
"async-task 1.3.1",
"crossbeam-channel 0.4.2",
"crossbeam-deque",
"crossbeam-utils 0.7.2",
@ -163,6 +163,12 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "async-task"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3"
[[package]]
name = "async-trait"
version = "0.1.30"
@ -1050,16 +1056,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "futures-core"
version = "0.3.4"
name = "futures"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
[[package]]
name = "futures-executor"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
[[package]]
name = "futures-macro"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
]
[[package]]
name = "futures-sink"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
[[package]]
name = "futures-task"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
dependencies = [
"once_cell",
]
[[package]]
name = "futures-timer"
@ -1067,6 +1136,26 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6"
[[package]]
name = "futures-util"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
[[package]]
name = "getrandom"
version = "0.1.14"
@ -2165,6 +2254,26 @@ dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c3bfbfb5bb42f99498c7234bbd768c220eb0cea6818259d0d18a1aa3d2595d"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccbf6449dcfb18562c015526b085b8df1aa3cdab180af8ec2ebd300a3bd28f63"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn 1.0.18",
]
[[package]]
name = "pin-project-lite"
version = "0.1.4"
@ -2177,6 +2286,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "piper"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6d62a6ea407d82215154475927b288219b79c8670e3371166210328e758ebaa"
dependencies = [
"crossbeam-utils 0.7.2",
"futures",
]
[[package]]
name = "pkg-config"
version = "0.3.17"
@ -2202,6 +2321,7 @@ dependencies = [
"anyhow",
"bitflags 1.2.1",
"filedescriptor",
"futures",
"lazy_static",
"libc",
"log",
@ -2210,6 +2330,7 @@ dependencies = [
"serial",
"shared_library",
"shell-words",
"smol",
"ssh2",
"uds_windows",
"winapi 0.3.8",
@ -2286,6 +2407,12 @@ version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
[[package]]
name = "proc-macro-nested"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
[[package]]
name = "proc-macro2"
version = "0.4.30"
@ -2310,7 +2437,7 @@ version = "0.2.0"
dependencies = [
"anyhow",
"async-std",
"async-task",
"async-task 1.3.1",
"lazy_static",
"thiserror",
"tokio",
@ -2653,6 +2780,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "scoped-tls-hkt"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e9d7eaddb227e8fbaaa71136ae0e1e913ca159b86c7da82f3e8f0044ad3a63"
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
@ -2876,6 +3009,24 @@ dependencies = [
"wayland-protocols",
]
[[package]]
name = "smol"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92bf48a84965d40061dbd596c17cfe0bff734b5aaf94d9408b0b3e382f7c06e"
dependencies = [
"async-task 3.0.0",
"crossbeam",
"futures",
"nix",
"once_cell",
"piper",
"scoped-tls-hkt",
"slab",
"socket2",
"wepoll-binding",
]
[[package]]
name = "socket2"
version = "0.3.12"
@ -3611,13 +3762,32 @@ dependencies = [
"webpki",
]
[[package]]
name = "wepoll-binding"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "720cc52662740f7097c7bbc15f7906e4d3f6e64e5ec70e84eb4690b70ce1afc5"
dependencies = [
"bitflags 1.2.1",
"wepoll-sys",
]
[[package]]
name = "wepoll-sys"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9082a777aed991f6769e2b654aa0cb29f1c3d615daf009829b07b66c7aff6a24"
dependencies = [
"cc",
]
[[package]]
name = "wezterm"
version = "0.1.0"
dependencies = [
"allsorts",
"anyhow",
"async-task",
"async-task 1.3.1",
"async-trait",
"base64 0.10.1",
"base91",
@ -3740,7 +3910,7 @@ name = "window"
version = "0.1.0"
dependencies = [
"anyhow",
"async-task",
"async-task 1.3.1",
"bitflags 1.2.1",
"cgl",
"clipboard",

View File

@ -37,3 +37,7 @@ winapi = { version = "0.3", features = [
"namedpipeapi",
"synchapi",
]}
[dev-dependencies]
smol = "0.1"
futures = "0.3"

View File

@ -0,0 +1,60 @@
use anyhow::anyhow;
use futures::prelude::*;
use portable_pty::{native_pty_system, CommandBuilder, PtySize};
// This example shows how to use the `smol` crate to use portable_pty
// in an asynchronous application.
fn main() -> anyhow::Result<()> {
smol::run(async {
let pty_system = native_pty_system();
let pair = pty_system.openpty(PtySize {
rows: 24,
cols: 80,
pixel_width: 0,
pixel_height: 0,
})?;
let cmd = CommandBuilder::new("whoami");
// Move the slave to another thread to block and spawn a
// command.
// Note that this implicitly drops slave and closes out
// file handles which is important to avoid deadlock
// when waiting for the child process!
let slave = pair.slave;
let mut child = smol::blocking!(slave.spawn_command(cmd))?;
let reader = pair.master.try_clone_reader()?;
println!(
"child status: {:?}",
smol::blocking!(child
.wait()
.map_err(|e| anyhow!("waiting for child: {}", e)))?
);
// We hold handles on the pty. Now that the child is complete
// there are no processes remaining that will write to it until
// we spawn more. We're not going to do that in this example,
// so we should close it down. If we didn't drop it explicitly
// here, then the attempt to read its output would block forever
// waiting for a future child that will never be spawned.
drop(pair.master);
let mut lines = futures::io::BufReader::new(smol::reader(reader)).lines();
while let Some(line) = lines.next().await {
let line = line.map_err(|e| anyhow!("problem reading line: {}", e))?;
// We print with escapes escaped because the windows conpty
// implementation synthesizes title change escape sequences
// in the output stream and it can be confusing to see those
// printed out raw in another terminal.
print!("output: len={} ", line.len());
for c in line.escape_debug() {
print!("{}", c);
}
println!();
}
Ok(())
})
}

View File

@ -124,7 +124,7 @@ pub trait Child: std::fmt::Debug {
/// Can be used to spawn processes into the pty.
pub trait SlavePty {
/// Spawns the command specified by the provided CommandBuilder
fn spawn_command(&self, cmd: CommandBuilder) -> Result<Box<dyn Child>, Error>;
fn spawn_command(&self, cmd: CommandBuilder) -> Result<Box<dyn Child + Send>, Error>;
}
/// Represents the exit status of a child process.
@ -159,8 +159,8 @@ impl From<std::process::ExitStatus> for ExitStatus {
pub struct PtyPair {
// slave is listed first so that it is dropped first.
// The drop order is stable and specified by rust rfc 1857
pub slave: Box<dyn SlavePty>,
pub master: Box<dyn MasterPty>,
pub slave: Box<dyn SlavePty + Send>,
pub master: Box<dyn MasterPty + Send>,
}
/// The `PtySystem` trait allows an application to work with multiple

View File

@ -99,7 +99,7 @@ struct Slave {
}
impl SlavePty for Slave {
fn spawn_command(&self, cmd: CommandBuilder) -> anyhow::Result<Box<dyn Child>> {
fn spawn_command(&self, cmd: CommandBuilder) -> anyhow::Result<Box<dyn Child + Send>> {
ensure!(
cmd.is_default_prog(),
"can only use default prog commands with serial tty implementations"

View File

@ -190,7 +190,7 @@ struct SshSlave {
}
impl SlavePty for SshSlave {
fn spawn_command(&self, cmd: CommandBuilder) -> anyhow::Result<Box<dyn Child>> {
fn spawn_command(&self, cmd: CommandBuilder) -> anyhow::Result<Box<dyn Child + Send>> {
self.pty.with_channel(|channel| {
for (key, val) in cmd.iter_env_as_str() {
channel
@ -205,7 +205,7 @@ impl SlavePty for SshSlave {
channel.exec(&command)?;
}
let child: Box<dyn Child> = Box::new(SshChild {
let child: Box<dyn Child + Send> = Box::new(SshChild {
pty: self.pty.clone(),
});

View File

@ -225,7 +225,7 @@ fn cloexec(fd: RawFd) -> Result<(), Error> {
}
impl SlavePty for UnixSlavePty {
fn spawn_command(&self, builder: CommandBuilder) -> Result<Box<dyn Child>, Error> {
fn spawn_command(&self, builder: CommandBuilder) -> Result<Box<dyn Child + Send>, Error> {
Ok(Box::new(self.fd.spawn_command(builder)?))
}
}

View File

@ -112,7 +112,7 @@ impl io::Write for ConPtyMasterPty {
}
impl SlavePty for ConPtySlavePty {
fn spawn_command(&self, cmd: CommandBuilder) -> anyhow::Result<Box<dyn Child>> {
fn spawn_command(&self, cmd: CommandBuilder) -> anyhow::Result<Box<dyn Child + Send>> {
let inner = self.inner.lock().unwrap();
let child = inner.con.spawn_command(cmd)?;
Ok(Box::new(child))