1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! This crate provides the ability to spawn processes with a function similar
//! to `thread::spawn`.
//!
//! Unlike `thread::spawn` data cannot be passed by the use of closures.  Instead
//! if must be explicitly passed as serializable object (specifically it must be
//! [`serde`](https://serde.rs/) serializable).  The return value from the
//! spawned closure also must be serializable and can then be retrieved from
//! the returned join handle.
//!
//! If the spawned functiom causes a panic it will also be serialized across
//! the process boundaries.
//!
//! # Example
//!
//! First for all of this to work you need to invoke `procspawn::init` at a
//! point early in your program (somewhere at the beginning of the main function).
//! Whatever happens before that point also happens in your spawned functions.
//!
//! Subprocesses are by default invoked with the same arguments and environment
//! variables as the parent process.
//!
//! ```rust,no_run
//! procspawn::init();
//! ```
//!
//! Now you can start spawning functions:
//!
//! ```rust,no_run
//! let data = vec![1, 2, 3, 4];
//! let handle = procspawn::spawn(data, |data| {
//!     println!("Received data {:?}", &data);
//!     data.into_iter().sum::<i64>()
//! });
//! let result = handle.join().unwrap();
//!```
//!
//! Because `procspawn` will invoke a subprocess and there is currently no
//! reliable way to intercept `main` in Rust it's necessary for you to call
//! [`procspawn::init`](fn.init.html) explicitly an early time in the program.
//!
//! Alternatively you can use the [`ProcConfig`](struct.ProcConfig.html)
//! builder object to initialize the process which gives you some extra
//! abilities to customize the processes spawned.  This for instance lets you
//! disable the default panic handling.
//!
//! [`spawn`](fn.spawn.html) can pass arbitrary serializable data, including
//! IPC senders and receivers from the [`ipc-channel`](https://crates.io/crates/ipc-channel)
//! crate, down to the new process.
//!
//! # Pools
//!
//! The default way to spawn processes will start and stop processes constantly.
//! For more uses it's a better idea to spawn a [`Pool`](struct.Pool.html)
//! which will keep processes around for reuse.  Between calls the processes
//! will stay around which also means the can keep state between calls if
//! needed.
//!
//! # Panics
//!
//! By default panics are captured and serialized across process boundaries.
//! This requires that the `backtrace` crate is used with serialization support.
//! If you do not need this feature you can disable the `backtrace` crate and
//! disable panic handling through the [`ProcConfig`](struct.ProcConfig.html)
//! object.
//!
//! # Feature Flags
//!
//! The following feature flags exist:
//!
//! * `safe-shared-libraries`: this feature is enabled by default.  When this
//!   feature is disable then no validation about shared library load status
//!   is performed around IPC calls.  This is highly unsafe if shared libraries
//!   are being used and a function from a shared library is spawned.
//! * `backtrace`: this feature is enabled by default.  When in use then
//!   backtraces are captured with the `backtrace-rs` crate and serialized
//!   across process boundaries.
//! * `test-support`: when this feature is enabled procspawn can be used
//!   with rusttest.  See [`testing`](#testing) for more information.
//! * `json`: enables optional JSON serialization.  For more information see
//!   [Bincode Limitations](#bincode-limitations).
//!
//! # Bincode Limitations
//!
//! This crate uses [`bincode`](https://github.com/servo/bincode) internally
//! for inter process communication.  Bincode currently has some limitations
//! which make some serde features incompatible with it.  Most notably if you
//! use `#[serde(flatten)]` data cannot be sent across the processes.  To
//! work around this you can enable the `json` feature and wrap affected objects
//! in the [`Json`](serde/struct.Json.html) wrapper to force JSON serialization.
//!
//! # Testing
//!
//! Due to limitations of the rusttest testing system there are some
//! restrictions to how this crate operates.  First of all you need to enable
//! the `test-support` feature for `procspawn` to work with rusttest at all.
//! Secondly your tests need to invoke the
//! [`enable_test_support!`](macro.enable_test_support.html) macro once
//! top-level.
//!
//! With this done the following behavior applies:
//!
//! * Tests behave as if `procspawn::init` was called (that means with the
//!   default arguments).  Other configuration is not supported.
//! * procspawn will register a dummy test (named `procspawn_test_helper`)
//!   which doesn't do anything when called directly, but acts as the spawning
//!   helper for all `spawn` calls.
//! * stdout is silenced by default unless `--show-output` or `--nocapture`
//!   is passed to tests.
//! * when trying to spawn with intercepted `stdout` be aware that there is
//!   extra noise that will be emitted by rusttest.
//!
//! ```rust,no_run
//! procspawn::enable_test_support!();
//!
//! #[test]
//! fn test_basic() {
//!     let handle = procspawn::spawn((1, 2), |(a, b)| a + b);
//!     let value = handle.join().unwrap();
//!     assert_eq!(value, 3);
//! }
//! ```
//!
//! # Shared Libraries
//!
//! `procspawn` uses the [`findshlibs`](https://github.com/gimli-rs/findshlibs)
//! crate to determine where a function is located in memory in both processes.
//! If a shared library is not loaded in the subprocess (because for instance it
//! is loaded at runtime) then the call will fail.  Because this adds quite
//! some overhead over every call you can also disable the `safe-shared-libraries`
//! feature (which is on by default) in which case you are not allowed to
//! invoke functions from shared libraries and no validation is performed.
//!
//! This in normal circumstances should be okay but you need to validate this.
//! Spawning processes will be disabled if the feature is not enabled until
//! you call the [`assert_spawn_is_safe`](fn.assert_spawn_is_safe.html) function.
//!
//! # Macros
//!
//! Alternatively the [`spawn!`](macro.spawn.html) macro can be used which can
//! make passing more than one argument easier:
//!
//! ```rust,no_run
//! let a = 42u32;
//! let b = 23u32;
//! let c = 1;
//! let handle = procspawn::spawn!((a => base, b, mut c) || -> Result<_, ()> {
//!     c += 1;
//!     Ok(base + b + c)
//! });
//! ```
//!
//! # Platform Support
//!
//! Currently this crate only supports macOS and Linux because ipc-channel
//! itself does not support Windows yet.  Additionally the findshlibs which is
//! used for the `safe-shared-libraries` feature also does not yet support
//! Windows.
//!
//! # More Examples
//!
//! Here are some examples of `procspawn` in action:
//!
//! * [simple.rs](https://github.com/mitsuhiko/procspawn/blob/master/examples/simple.rs):
//!   a very simple example showing the basics.
//! * [args.rs](https://github.com/mitsuhiko/procspawn/blob/master/examples/args.rs):
//!   shows how arguments are available to the subprocess as well.
//! * [timeout.rs](https://github.com/mitsuhiko/procspawn/blob/master/examples/timeout.rs):
//!   shows how you can wait on a process with timeouts.
//! * [bad-serialization.rs](https://github.com/mitsuhiko/procspawn/blob/master/examples/bad-serialization.rs):
//!   shows JSON based workarounds for bincode limitations.
//! * [macro.rs](https://github.com/mitsuhiko/procspawn/blob/master/examples/macro.rs):
//!   demonstrates macro usage.
//!
//! More examples can be found in the example folder: [examples](https://github.com/mitsuhiko/procspawn/tree/master/examples)

#[macro_use]
mod proc;

mod core;
mod error;
mod panic;
mod pool;

#[cfg(feature = "json")]
mod json;

#[doc(hidden)]
pub mod testsupport;

pub mod serde;

mod macros;

pub use self::core::{assert_spawn_is_safe, init, ProcConfig};
pub use self::error::{Location, PanicInfo, SpawnError};
pub use self::pool::{Pool, PoolBuilder};
pub use self::proc::{spawn, Builder, JoinHandle};

#[cfg(feature = "async")]
pub use self::asyncsupport::{spawn_async, AsyncJoinHandle};