aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
authorDavid LeGare <legare@google.com>2022-03-03 02:20:55 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-03-03 02:20:55 +0000
commit0ea93d7d7c90197ee82b42cbdf27c5fbfe2c5ff3 (patch)
treefa75cb911f3976cb5994a09764373f0edd644abb /src/lib.rs
parentbfc892909c6dbec1c8e3f1e95ba1afeeb2c444b5 (diff)
parente53f7d4621501f8ffc32237d2599b9bb94709c33 (diff)
downloadshared_child-android13-qpr2-s3-release.tar.gz
Update shared_child to 1.0.0 am: 384a131edc am: 1a1ad39a5a am: e53f7d4621t_frc_odp_330442040t_frc_odp_330442000t_frc_ase_330444010android-13.0.0_r83android-13.0.0_r82android-13.0.0_r81android-13.0.0_r80android-13.0.0_r79android-13.0.0_r78android-13.0.0_r77android-13.0.0_r76android-13.0.0_r75android-13.0.0_r74android-13.0.0_r73android-13.0.0_r72android-13.0.0_r71android-13.0.0_r70android-13.0.0_r69android-13.0.0_r68android-13.0.0_r67android-13.0.0_r66android-13.0.0_r65android-13.0.0_r64android-13.0.0_r63android-13.0.0_r62android-13.0.0_r61android-13.0.0_r60android-13.0.0_r59android-13.0.0_r58android-13.0.0_r57android-13.0.0_r56android-13.0.0_r55android-13.0.0_r54android-13.0.0_r53android-13.0.0_r52android-13.0.0_r51android-13.0.0_r50android-13.0.0_r49android-13.0.0_r48android-13.0.0_r47android-13.0.0_r46android-13.0.0_r45android-13.0.0_r44android-13.0.0_r43android-13.0.0_r42android-13.0.0_r41android-13.0.0_r40android-13.0.0_r39android-13.0.0_r38android-13.0.0_r37android-13.0.0_r36android-13.0.0_r35android-13.0.0_r34android-13.0.0_r33android-13.0.0_r32android-13.0.0_r30android-13.0.0_r29android-13.0.0_r28android-13.0.0_r27android-13.0.0_r24android-13.0.0_r23android-13.0.0_r22android-13.0.0_r21android-13.0.0_r20android-13.0.0_r19android-13.0.0_r18android-13.0.0_r17android-13.0.0_r16aml_go_odp_330912000aml_go_ads_330915100aml_go_ads_330915000aml_go_ads_330913000android13-qpr3-s9-releaseandroid13-qpr3-s8-releaseandroid13-qpr3-s7-releaseandroid13-qpr3-s6-releaseandroid13-qpr3-s5-releaseandroid13-qpr3-s4-releaseandroid13-qpr3-s3-releaseandroid13-qpr3-s2-releaseandroid13-qpr3-s14-releaseandroid13-qpr3-s13-releaseandroid13-qpr3-s12-releaseandroid13-qpr3-s11-releaseandroid13-qpr3-s10-releaseandroid13-qpr3-s1-releaseandroid13-qpr3-releaseandroid13-qpr3-c-s8-releaseandroid13-qpr3-c-s7-releaseandroid13-qpr3-c-s6-releaseandroid13-qpr3-c-s5-releaseandroid13-qpr3-c-s4-releaseandroid13-qpr3-c-s3-releaseandroid13-qpr3-c-s2-releaseandroid13-qpr3-c-s12-releaseandroid13-qpr3-c-s11-releaseandroid13-qpr3-c-s10-releaseandroid13-qpr3-c-s1-releaseandroid13-qpr2-s9-releaseandroid13-qpr2-s8-releaseandroid13-qpr2-s7-releaseandroid13-qpr2-s6-releaseandroid13-qpr2-s5-releaseandroid13-qpr2-s3-releaseandroid13-qpr2-s2-releaseandroid13-qpr2-s12-releaseandroid13-qpr2-s11-releaseandroid13-qpr2-s10-releaseandroid13-qpr2-s1-releaseandroid13-qpr2-releaseandroid13-qpr2-b-s1-releaseandroid13-qpr1-s8-releaseandroid13-qpr1-s7-releaseandroid13-qpr1-s6-releaseandroid13-qpr1-s5-releaseandroid13-qpr1-s4-releaseandroid13-qpr1-s3-releaseandroid13-qpr1-s2-releaseandroid13-qpr1-s1-releaseandroid13-qpr1-releaseandroid13-mainline-go-adservices-releaseandroid13-frc-odp-releaseandroid13-devandroid13-d4-s2-releaseandroid13-d4-s1-releaseandroid13-d4-releaseandroid13-d3-s1-releaseandroid13-d2-release
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/shared_child/+/2005937 Change-Id: Ic4a38b1195da86e9f527ddfac146e120e55a41ae
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs140
1 files changed, 128 insertions, 12 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 58c1e0c..5c4f200 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -62,7 +62,7 @@
//! ```
use std::io;
-use std::process::{Child, Command, ExitStatus};
+use std::process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, ExitStatus};
use std::sync::{Condvar, Mutex};
mod sys;
@@ -85,16 +85,36 @@ pub struct SharedChild {
}
impl SharedChild {
- /// Spawn a new `SharedChild` from a `std::process::Command`.
- pub fn spawn(command: &mut Command) -> io::Result<SharedChild> {
+ /// Spawn a new `SharedChild` from a
+ /// [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html).
+ pub fn spawn(command: &mut Command) -> io::Result<Self> {
let child = command.spawn()?;
- Ok(SharedChild {
+ Ok(Self {
child: Mutex::new(child),
state_lock: Mutex::new(NotWaiting),
state_condvar: Condvar::new(),
})
}
+ /// Construct a new `SharedChild` from an already spawned
+ /// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html).
+ ///
+ /// This constructor needs to know whether `child` has already been waited on, and the only way
+ /// to find that out is to call `child.try_wait()` internally. If the child process is
+ /// currently a zombie, that call will clean it up as a side effect. The [`SharedChild::spawn`]
+ /// constructor doesn't need to do this.
+ pub fn new(mut child: Child) -> io::Result<Self> {
+ let state = match child.try_wait()? {
+ Some(status) => Exited(status),
+ None => NotWaiting,
+ };
+ Ok(Self {
+ child: Mutex::new(child),
+ state_lock: Mutex::new(state),
+ state_condvar: Condvar::new(),
+ })
+ }
+
/// Return the child process ID.
pub fn id(&self) -> u32 {
self.child.lock().unwrap().id()
@@ -204,15 +224,46 @@ impl SharedChild {
self.child.lock().unwrap().kill()
}
- /// Consume the `SharedChild` and return the `std::process::Child` it
- /// contains.
+ /// Consume the `SharedChild` and return the
+ /// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html)
+ /// it contains.
///
- /// We never reap the child process except through `Child::wait`, so the
- /// child object's inner state is correct, even if it was waited on while it
- /// was shared.
+ /// We never reap the child process except by calling `wait` or `try_wait`
+ /// on it, so the child object's inner state is correct, even if it was
+ /// waited on while it was shared.
pub fn into_inner(self) -> Child {
self.child.into_inner().unwrap()
}
+
+ /// Take the child's
+ /// [`stdin`](https://doc.rust-lang.org/std/process/struct.Child.html#structfield.stdin)
+ /// handle, if any.
+ ///
+ /// This will only return `Some` the first time it's called, and then only if the `Command`
+ /// that created the child was configured with `.stdin(Stdio::piped())`.
+ pub fn take_stdin(&self) -> Option<ChildStdin> {
+ self.child.lock().unwrap().stdin.take()
+ }
+
+ /// Take the child's
+ /// [`stdout`](https://doc.rust-lang.org/std/process/struct.Child.html#structfield.stdout)
+ /// handle, if any.
+ ///
+ /// This will only return `Some` the first time it's called, and then only if the `Command`
+ /// that created the child was configured with `.stdout(Stdio::piped())`.
+ pub fn take_stdout(&self) -> Option<ChildStdout> {
+ self.child.lock().unwrap().stdout.take()
+ }
+
+ /// Take the child's
+ /// [`stderr`](https://doc.rust-lang.org/std/process/struct.Child.html#structfield.stderr)
+ /// handle, if any.
+ ///
+ /// This will only return `Some` the first time it's called, and then only if the `Command`
+ /// that created the child was configured with `.stderr(Stdio::piped())`.
+ pub fn take_stderr(&self) -> Option<ChildStderr> {
+ self.child.lock().unwrap().stderr.take()
+ }
}
#[derive(Debug)]
@@ -226,9 +277,9 @@ use crate::ChildState::*;
#[cfg(test)]
mod tests {
- use super::{sys, SharedChild};
- use std;
- use std::process::Command;
+ use super::*;
+ use std::error::Error;
+ use std::process::{Command, Stdio};
use std::sync::Arc;
// Python isn't available on some Unix platforms, e.g. Android, so we need this instead.
@@ -244,6 +295,7 @@ mod tests {
cmd
}
+ // Python isn't available on some Unix platforms, e.g. Android, so we need this instead.
#[cfg(unix)]
pub fn sleep_forever_cmd() -> Command {
let mut cmd = Command::new("sleep");
@@ -258,6 +310,19 @@ mod tests {
cmd
}
+ // Python isn't available on some Unix platforms, e.g. Android, so we need this instead.
+ #[cfg(unix)]
+ pub fn cat_cmd() -> Command {
+ Command::new("cat")
+ }
+
+ #[cfg(not(unix))]
+ pub fn cat_cmd() -> Command {
+ let mut cmd = Command::new("python");
+ cmd.arg("-c").arg("");
+ cmd
+ }
+
#[test]
fn test_wait() {
let child = SharedChild::spawn(&mut true_cmd()).unwrap();
@@ -345,4 +410,55 @@ mod tests {
// But wait should succeed.
child.wait().unwrap();
}
+
+ #[test]
+ fn test_new() -> Result<(), Box<dyn Error>> {
+ // Spawn a short-lived child.
+ let mut command = cat_cmd();
+ command.stdin(Stdio::piped());
+ command.stdout(Stdio::null());
+ let mut child = command.spawn()?;
+ let child_stdin = child.stdin.take().unwrap();
+
+ // Construct a SharedChild from the Child, which has not yet been waited on. The child is
+ // blocked on stdin, so we know it hasn't yet exited.
+ let mut shared_child = SharedChild::new(child).unwrap();
+ assert!(matches!(
+ *shared_child.state_lock.lock().unwrap(),
+ NotWaiting,
+ ));
+
+ // Now close the child's stdin. This will cause the child to exit.
+ drop(child_stdin);
+
+ // Construct more SharedChild objects from the same child, in a loop. Eventually one of
+ // them will notice that the child has exited.
+ loop {
+ shared_child = SharedChild::new(shared_child.into_inner())?;
+ if let Exited(status) = &*shared_child.state_lock.lock().unwrap() {
+ assert!(status.success());
+ return Ok(());
+ }
+ }
+ }
+
+ #[test]
+ fn test_takes() -> Result<(), Box<dyn Error>> {
+ let mut command = true_cmd();
+ command.stdin(Stdio::piped());
+ command.stdout(Stdio::piped());
+ command.stderr(Stdio::piped());
+ let shared_child = SharedChild::spawn(&mut command)?;
+
+ assert!(shared_child.take_stdin().is_some());
+ assert!(shared_child.take_stdout().is_some());
+ assert!(shared_child.take_stderr().is_some());
+
+ assert!(shared_child.take_stdin().is_none());
+ assert!(shared_child.take_stdout().is_none());
+ assert!(shared_child.take_stderr().is_none());
+
+ shared_child.wait()?;
+ Ok(())
+ }
}