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
//! This crate provides the ability to spawn processes with a function similar //! to `thread::spawn`. //! //! Unlike `thread::spawn` data cannot be passed in closures but must be //! explicitly passed as single argument which must be [`serde`](https://serde.rs/) //! serializable. The return value from the spawned closure also must be //! serializable and can then be unwrapped from the returned join handle. //! If your function has data enclosed it will panic at runtime. //! //! ```rust,no_run //! // call this early in your main() function. This is where all spawned //! // functions will be invoked. //! procspawn::init(); //! //! 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) at an early time in the program. The //! place where this will be called is the entrypoint for the subprocesses //! spawned. The subprocess is invoked with the same arguments and environment //! variables by default. //! //! [`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 [`enable_test_support!`](macro.enable_test_support.html) //! 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`](struct.Json.html) wrapper to force JSON serialization. //! //! # 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. //! //! # 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. //! //! # 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. //! //! More examples can be found in the example folder: [examples](https://github.com/mitsuhiko/procspawn/tree/master/examples) use serde::{Deserialize, Serialize}; #[macro_use] mod proc; mod core; mod error; mod panic; mod pool; #[cfg(feature = "json")] mod json; #[doc(hidden)] pub mod testsupport; 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::{Builder, JoinHandle}; #[cfg(feature = "json")] pub use self::json::Json; /// Spawn a new process to run a function with some payload. /// /// ```rust,no_run /// // call this early in your main() function. This is where all spawned /// // functions will be invoked. /// procspawn::init(); /// /// 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(); /// ``` pub fn spawn<A: Serialize + for<'de> Deserialize<'de>, R: Serialize + for<'de> Deserialize<'de>>( args: A, f: fn(A) -> R, ) -> JoinHandle<R> { Builder::new().spawn(args, f) }