Snap for 8249732 from 0ea93d7d7c90197ee82b42cbdf27c5fbfe2c5ff3 to tm-release

Change-Id: I14e5c68beaad8f94685f99f3e11606969c2d8f6d
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index a082427..ea16222 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "9ff8c8e9d6dc8da25a7f7947f4488ceecdffa553"
+    "sha1": "ed1c4f6eed646608390a4130bb6de78d630a7b0f"
   }
 }
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..8a05d56
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,31 @@
+name: tests
+
+on:
+  push:
+    branches:
+      - "*"
+    # not on tags
+  pull_request:
+
+env:
+  RUSTFLAGS: "-D warnings"
+  RUST_BACKTRACE: "1"
+
+jobs:
+  cargo_tests:
+    name: ${{ matrix.os }} ${{ matrix.rust_channel }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: ["ubuntu-latest", "macOS-latest", "windows-latest"]
+        rust_channel: [stable, beta, nightly]
+
+    steps:
+    - uses: actions/checkout@v1
+    - uses: actions-rs/toolchain@v1
+      with:
+        toolchain: ${{ matrix.rust_channel }}
+        profile: minimal
+        override: true
+    - run: cargo test
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 0d4ad53..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-language: rust
-os:
-  - linux
-  - osx
-rust:
-  - stable
-  - beta
-  - nightly
diff --git a/Android.bp b/Android.bp
index 02272a3..60a4abd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,7 +23,7 @@
     host_supported: true,
     crate_name: "shared_child",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.3.5",
+    cargo_pkg_version: "1.0.0",
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
@@ -41,7 +41,7 @@
     host_supported: true,
     crate_name: "shared_child",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.3.5",
+    cargo_pkg_version: "1.0.0",
     srcs: ["src/lib.rs"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
diff --git a/Cargo.toml b/Cargo.toml
index 75e890b..d13367c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,17 +3,16 @@
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
 #
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
 
 [package]
 edition = "2018"
 name = "shared_child"
-version = "0.3.5"
+version = "1.0.0"
 authors = ["jacko"]
 description = "a library for using child processes from multiple threads"
 documentation = "https://docs.rs/shared_child"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 5c8ea27..2453bc6 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "shared_child"
-version = "0.3.5"
+version = "1.0.0"
 authors = ["jacko"]
 license = "MIT"
 repository = "https://github.com/oconnor663/shared_child.rs"
diff --git a/METADATA b/METADATA
index 2c67826..029d963 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/shared_child/shared_child-0.3.5.crate"
+    value: "https://static.crates.io/crates/shared_child/shared_child-1.0.0.crate"
   }
-  version: "0.3.5"
+  version: "1.0.0"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2021
-    month: 4
-    day: 13
+    year: 2022
+    month: 3
+    day: 1
   }
 }
diff --git a/README.md b/README.md
index 94525fa..b08106d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# shared_child.rs [![Travis build](https://travis-ci.org/oconnor663/shared_child.rs.svg?branch=master)](https://travis-ci.org/oconnor663/shared_child.rs) [![Build status](https://ci.appveyor.com/api/projects/status/900ckow3c5awq3t5/branch/master?svg=true)](https://ci.appveyor.com/project/oconnor663/shared-child-rs/branch/master) [![crates.io](https://img.shields.io/crates/v/shared_child.svg)](https://crates.io/crates/shared_child) [![docs.rs](https://docs.rs/shared_child/badge.svg)](https://docs.rs/shared_child)
+# shared_child.rs [![Actions Status](https://github.com/oconnor663/shared_child.rs/workflows/tests/badge.svg)](https://github.com/oconnor663/shared_child.rs/actions) [![crates.io](https://img.shields.io/crates/v/shared_child.svg)](https://crates.io/crates/shared_child) [![docs.rs](https://docs.rs/shared_child/badge.svg)](https://docs.rs/shared_child)
 
 A library for awaiting and killing child processes from multiple threads.
 
diff --git a/README.tpl b/README.tpl
index 3ad7e39..c16ee7f 100644
--- a/README.tpl
+++ b/README.tpl
@@ -1,3 +1,3 @@
-# {{crate}}.rs [![Travis build](https://travis-ci.org/oconnor663/shared_child.rs.svg?branch=master)](https://travis-ci.org/oconnor663/shared_child.rs) [![Build status](https://ci.appveyor.com/api/projects/status/900ckow3c5awq3t5/branch/master?svg=true)](https://ci.appveyor.com/project/oconnor663/shared-child-rs/branch/master) [![crates.io](https://img.shields.io/crates/v/shared_child.svg)](https://crates.io/crates/shared_child) [![docs.rs](https://docs.rs/shared_child/badge.svg)](https://docs.rs/shared_child)
+# {{crate}}.rs [![Actions Status](https://github.com/oconnor663/shared_child.rs/workflows/tests/badge.svg)](https://github.com/oconnor663/shared_child.rs/actions) [![crates.io](https://img.shields.io/crates/v/shared_child.svg)](https://crates.io/crates/shared_child) [![docs.rs](https://docs.rs/shared_child/badge.svg)](https://docs.rs/shared_child)
 
 {{readme}}
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 354716e..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-environment:
-  matrix:
-  - TARGET: x86_64-pc-windows-msvc
-    VERSION: 1.31.0
-  - TARGET: i686-pc-windows-msvc
-    VERSION: 1.31.0
-  - TARGET: i686-pc-windows-gnu
-    VERSION: 1.31.0
-  - TARGET: x86_64-pc-windows-msvc
-    VERSION: beta
-  - TARGET: i686-pc-windows-msvc
-    VERSION: beta
-  - TARGET: i686-pc-windows-gnu
-    VERSION: beta
-  - TARGET: x86_64-pc-windows-msvc
-    VERSION: nightly
-  - TARGET: i686-pc-windows-msvc
-    VERSION: nightly
-  - TARGET: i686-pc-windows-gnu
-    VERSION: nightly
-install:
-  - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:VERSION}-${env:TARGET}.exe"
-  - rust-%VERSION%-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
-  - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
-  - SET PATH=%PATH%;C:\MinGW\bin
-  - rustc -V
-  - cargo -V
-
-build: false
-
-test_script:
-  - cargo test --verbose
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 @@
 }
 
 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 @@
         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 @@
 
 #[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 @@
         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 @@
         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 @@
         // 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(())
+    }
 }
diff --git a/src/sys/unix.rs b/src/sys/unix.rs
index 400f910..abdfa48 100644
--- a/src/sys/unix.rs
+++ b/src/sys/unix.rs
@@ -48,9 +48,6 @@
         //   invocation, then check for a non-zero value afterwards.
         //
         // https://github.com/opensource-apple/xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/bsd/kern/kern_exit.c#L2150-L2156
-        //
-        // XXX: The siginfo_t struct has padding. Does that make it unsound to
-        // initialize it this way?
         siginfo = std::mem::zeroed();
         libc::waitid(
             libc::P_PID,