Created
April 22, 2019 23:57
-
-
Save Technius/43977937a28e8846d917b53605e32cc3 to your computer and use it in GitHub Desktop.
Asynchronous process I/O in Rust using tokio
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This code sample needs the dependencies: | |
// tokio = "0.1" | |
// tokio-process = "0.2" | |
use tokio::prelude::*; | |
use std::process::{Command, Stdio}; | |
use tokio_process::CommandExt; | |
use std::time::Duration; | |
fn main() { | |
// We'll start our process here. | |
let mut child_process = | |
Command::new("sleep") | |
.arg("10") | |
.stdout(Stdio::piped()) | |
// tokio-process extends Command with several methods that will spawn | |
// the process asynchronously. Here, it will spawn a | |
// tokio_process::Child, which supports asynchronous I/O. | |
.spawn_async() | |
.expect("Could not spawn process"); | |
let out = child_process.stdout() // Let's try to get the stdout handle. | |
// This is a &mut Option, so remove the Option value | |
.take() | |
// and unwrap the ChildStdout | |
.unwrap(); | |
// This is how long we're willing to wait for the child process to respond. | |
// We'll be using this later. | |
let timeout_length = Duration::from_secs(5); | |
// Now, we want to read from our process. | |
// A tokio::codec::Codec can be used to decode the process output into a | |
// type that we want. Here, we'll use a LinesCodec that will transform the | |
// output bytes into lines of text. | |
let lines_codec = tokio::codec::LinesCodec::new(); | |
// Next, we'll create a Future (a description of an asynchronous | |
// computation) that will represent the output (or lack thereof) of our | |
// child process. | |
let line_fut = | |
// We'll read the process output using our codec, producing a Stream | |
tokio::codec::FramedRead::new(out, lines_codec) | |
// We only want the first line, so we'll only take that from the Stream | |
.take(1) | |
// Gather the stream elements (in this case, just a line) into a Future | |
// of a Vec | |
.collect() | |
// Then, we'll try to take the first item from the results. | |
.map(|lines| lines.first().cloned()) | |
// We'll use the timeout length we've defined above to ensure our Future | |
// fails if the process doesn't respond | |
.timeout(timeout_length); | |
// Now, we'll execute our computation. Here, I'll demonstrate how you can | |
// get the results back on your main thread. If you don't need to, you can | |
// use tokio::run to execute your Future, which will basically do the same | |
// as what will follow anyways. | |
// We will need a tokio Runtime that can execute our Future. Here's one way | |
// to obtain one: | |
let mut runtime = tokio::runtime::Runtime::new().expect("Could not create tokio runtime"); | |
// Finally, we can execute our Future and wait for it to finish. | |
let result = runtime.block_on(line_fut); | |
match result { | |
Ok(Some(line)) => println!("Got output: {}", line), | |
Ok(None) => println!("No output received"), | |
Err(err) => println!("Encountered error: {}", err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment