README.md
1# 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)
2
3A library for awaiting and killing child processes from multiple threads.
4
5- [Docs](https://docs.rs/shared_child)
6- [Crate](https://crates.io/crates/shared_child)
7- [Repo](https://github.com/oconnor663/shared_child.rs)
8
9The
10[`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html)
11type in the standard library provides
12[`wait`](https://doc.rust-lang.org/std/process/struct.Child.html#method.wait)
13and
14[`kill`](https://doc.rust-lang.org/std/process/struct.Child.html#method.kill)
15methods that take `&mut self`, making it impossible to kill a child process
16while another thread is waiting on it. That design works around a race
17condition in Unix's `waitpid` function, where a PID might get reused as soon
18as the wait returns, so a signal sent around the same time could
19accidentally get delivered to the wrong process.
20
21However with the newer POSIX `waitid` function, we can wait on a child
22without freeing its PID for reuse. That makes it safe to send signals
23concurrently. Windows has actually always supported this, by preventing PID
24reuse while there are still open handles to a child process. This library
25wraps `std::process::Child` for concurrent use, backed by these APIs.
26
27Compatibility note: The `libc` crate doesn't currently support `waitid` on
28NetBSD or OpenBSD, or on older versions of OSX. There [might also
29be](https://bugs.python.org/msg167016) some version of OSX where the
30`waitid` function exists but is broken. We can add a "best effort"
31workaround using `waitpid` for these platforms as we run into them. Please
32[file an issue](https://github.com/oconnor663/shared_child.rs/issues/new) if
33you hit this.
34
35## Example
36
37```rust
38use shared_child::SharedChild;
39use std::process::Command;
40use std::sync::Arc;
41
42// Spawn a child that will just sleep for a long time,
43// and put it in an Arc to share between threads.
44let mut command = Command::new("python");
45command.arg("-c").arg("import time; time.sleep(1000000000)");
46let shared_child = SharedChild::spawn(&mut command).unwrap();
47let child_arc = Arc::new(shared_child);
48
49// On another thread, wait on the child process.
50let child_arc_clone = child_arc.clone();
51let thread = std::thread::spawn(move || {
52 child_arc_clone.wait().unwrap()
53});
54
55// While the other thread is waiting, kill the child process.
56// This wouldn't be possible with e.g. Arc<Mutex<Child>> from
57// the standard library, because the waiting thread would be
58// holding the mutex.
59child_arc.kill().unwrap();
60
61// Join the waiting thread and get the exit status.
62let exit_status = thread.join().unwrap();
63assert!(!exit_status.success());
64```
65
README.tpl