| //! Rayon-core houses the core stable APIs of Rayon. |
| //! |
| //! These APIs have been mirrored in the Rayon crate and it is recommended to use these from there. |
| //! |
| //! [`join`] is used to take two closures and potentially run them in parallel. |
| //! - It will run in parallel if task B gets stolen before task A can finish. |
| //! - It will run sequentially if task A finishes before task B is stolen and can continue on task B. |
| //! |
| //! [`scope`] creates a scope in which you can run any number of parallel tasks. |
| //! These tasks can spawn nested tasks and scopes, but given the nature of work stealing, the order of execution can not be guaranteed. |
| //! The scope will exist until all tasks spawned within the scope have been completed. |
| //! |
| //! [`spawn`] add a task into the 'static' or 'global' scope, or a local scope created by the [`scope()`] function. |
| //! |
| //! [`ThreadPool`] can be used to create your own thread pools (using [`ThreadPoolBuilder`]) or to customize the global one. |
| //! Tasks spawned within the pool (using [`install()`], [`join()`], etc.) will be added to a deque, |
| //! where it becomes available for work stealing from other threads in the local threadpool. |
| //! |
| //! [`join`]: fn.join.html |
| //! [`scope`]: fn.scope.html |
| //! [`scope()`]: fn.scope.html |
| //! [`spawn`]: fn.spawn.html |
| //! [`ThreadPool`]: struct.threadpool.html |
| //! [`install()`]: struct.ThreadPool.html#method.install |
| //! [`spawn()`]: struct.ThreadPool.html#method.spawn |
| //! [`join()`]: struct.ThreadPool.html#method.join |
| //! [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html |
| //! |
| //! ## Restricting multiple versions |
| //! |
| //! In order to ensure proper coordination between threadpools, and especially |
| //! to make sure there's only one global threadpool, `rayon-core` is actively |
| //! restricted from building multiple versions of itself into a single target. |
| //! You may see a build error like this in violation: |
| //! |
| //! ```text |
| //! error: native library `rayon-core` is being linked to by more |
| //! than one package, and can only be linked to by one package |
| //! ``` |
| //! |
| //! While we strive to keep `rayon-core` semver-compatible, it's still |
| //! possible to arrive at this situation if different crates have overly |
| //! restrictive tilde or inequality requirements for `rayon-core`. The |
| //! conflicting requirements will need to be resolved before the build will |
| //! succeed. |
| |
| #![doc(html_root_url = "https://docs.rs/rayon-core/1.9")] |
| #![deny(missing_debug_implementations)] |
| #![deny(missing_docs)] |
| #![deny(unreachable_pub)] |
| #![warn(rust_2018_idioms)] |
| |
| use std::any::Any; |
| use std::env; |
| use std::error::Error; |
| use std::fmt; |
| use std::io; |
| use std::marker::PhantomData; |
| use std::str::FromStr; |
| |
| #[macro_use] |
| mod log; |
| #[macro_use] |
| mod private; |
| |
| mod job; |
| mod join; |
| mod latch; |
| mod registry; |
| mod scope; |
| mod sleep; |
| mod spawn; |
| mod thread_pool; |
| mod unwind; |
| |
| mod compile_fail; |
| mod test; |
| |
| pub use self::join::{join, join_context}; |
| pub use self::registry::ThreadBuilder; |
| pub use self::scope::{in_place_scope, scope, Scope}; |
| pub use self::scope::{in_place_scope_fifo, scope_fifo, ScopeFifo}; |
| pub use self::spawn::{spawn, spawn_fifo}; |
| pub use self::thread_pool::current_thread_has_pending_tasks; |
| pub use self::thread_pool::current_thread_index; |
| pub use self::thread_pool::ThreadPool; |
| |
| use self::registry::{CustomSpawn, DefaultSpawn, ThreadSpawn}; |
| |
| /// Returns the number of threads in the current registry. If this |
| /// code is executing within a Rayon thread-pool, then this will be |
| /// the number of threads for the thread-pool of the current |
| /// thread. Otherwise, it will be the number of threads for the global |
| /// thread-pool. |
| /// |
| /// This can be useful when trying to judge how many times to split |
| /// parallel work (the parallel iterator traits use this value |
| /// internally for this purpose). |
| /// |
| /// # Future compatibility note |
| /// |
| /// Note that unless this thread-pool was created with a |
| /// builder that specifies the number of threads, then this |
| /// number may vary over time in future versions (see [the |
| /// `num_threads()` method for details][snt]). |
| /// |
| /// [snt]: struct.ThreadPoolBuilder.html#method.num_threads |
| pub fn current_num_threads() -> usize { |
| crate::registry::Registry::current_num_threads() |
| } |
| |
| /// Error when initializing a thread pool. |
| #[derive(Debug)] |
| pub struct ThreadPoolBuildError { |
| kind: ErrorKind, |
| } |
| |
| #[derive(Debug)] |
| enum ErrorKind { |
| GlobalPoolAlreadyInitialized, |
| IOError(io::Error), |
| } |
| |
| /// Used to create a new [`ThreadPool`] or to configure the global rayon thread pool. |
| /// ## Creating a ThreadPool |
| /// The following creates a thread pool with 22 threads. |
| /// |
| /// ```rust |
| /// # use rayon_core as rayon; |
| /// let pool = rayon::ThreadPoolBuilder::new().num_threads(22).build().unwrap(); |
| /// ``` |
| /// |
| /// To instead configure the global thread pool, use [`build_global()`]: |
| /// |
| /// ```rust |
| /// # use rayon_core as rayon; |
| /// rayon::ThreadPoolBuilder::new().num_threads(22).build_global().unwrap(); |
| /// ``` |
| /// |
| /// [`ThreadPool`]: struct.ThreadPool.html |
| /// [`build_global()`]: struct.ThreadPoolBuilder.html#method.build_global |
| pub struct ThreadPoolBuilder<S = DefaultSpawn> { |
| /// The number of threads in the rayon thread pool. |
| /// If zero will use the RAYON_NUM_THREADS environment variable. |
| /// If RAYON_NUM_THREADS is invalid or zero will use the default. |
| num_threads: usize, |
| |
| /// Custom closure, if any, to handle a panic that we cannot propagate |
| /// anywhere else. |
| panic_handler: Option<Box<PanicHandler>>, |
| |
| /// Closure to compute the name of a thread. |
| get_thread_name: Option<Box<dyn FnMut(usize) -> String>>, |
| |
| /// The stack size for the created worker threads |
| stack_size: Option<usize>, |
| |
| /// Closure invoked on worker thread start. |
| start_handler: Option<Box<StartHandler>>, |
| |
| /// Closure invoked on worker thread exit. |
| exit_handler: Option<Box<ExitHandler>>, |
| |
| /// Closure invoked to spawn threads. |
| spawn_handler: S, |
| |
| /// If false, worker threads will execute spawned jobs in a |
| /// "depth-first" fashion. If true, they will do a "breadth-first" |
| /// fashion. Depth-first is the default. |
| breadth_first: bool, |
| } |
| |
| /// Contains the rayon thread pool configuration. Use [`ThreadPoolBuilder`] instead. |
| /// |
| /// [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html |
| #[deprecated(note = "Use `ThreadPoolBuilder`")] |
| pub struct Configuration { |
| builder: ThreadPoolBuilder, |
| } |
| |
| /// The type for a panic handling closure. Note that this same closure |
| /// may be invoked multiple times in parallel. |
| type PanicHandler = dyn Fn(Box<dyn Any + Send>) + Send + Sync; |
| |
| /// The type for a closure that gets invoked when a thread starts. The |
| /// closure is passed the index of the thread on which it is invoked. |
| /// Note that this same closure may be invoked multiple times in parallel. |
| type StartHandler = dyn Fn(usize) + Send + Sync; |
| |
| /// The type for a closure that gets invoked when a thread exits. The |
| /// closure is passed the index of the thread on which is is invoked. |
| /// Note that this same closure may be invoked multiple times in parallel. |
| type ExitHandler = dyn Fn(usize) + Send + Sync; |
| |
| // NB: We can't `#[derive(Default)]` because `S` is left ambiguous. |
| impl Default for ThreadPoolBuilder { |
| fn default() -> Self { |
| ThreadPoolBuilder { |
| num_threads: 0, |
| panic_handler: None, |
| get_thread_name: None, |
| stack_size: None, |
| start_handler: None, |
| exit_handler: None, |
| spawn_handler: DefaultSpawn, |
| breadth_first: false, |
| } |
| } |
| } |
| |
| impl ThreadPoolBuilder { |
| /// Creates and returns a valid rayon thread pool builder, but does not initialize it. |
| pub fn new() -> Self { |
| Self::default() |
| } |
| } |
| |
| /// Note: the `S: ThreadSpawn` constraint is an internal implementation detail for the |
| /// default spawn and those set by [`spawn_handler`](#method.spawn_handler). |
| impl<S> ThreadPoolBuilder<S> |
| where |
| S: ThreadSpawn, |
| { |
| /// Creates a new `ThreadPool` initialized using this configuration. |
| pub fn build(self) -> Result<ThreadPool, ThreadPoolBuildError> { |
| ThreadPool::build(self) |
| } |
| |
| /// Initializes the global thread pool. This initialization is |
| /// **optional**. If you do not call this function, the thread pool |
| /// will be automatically initialized with the default |
| /// configuration. Calling `build_global` is not recommended, except |
| /// in two scenarios: |
| /// |
| /// - You wish to change the default configuration. |
| /// - You are running a benchmark, in which case initializing may |
| /// yield slightly more consistent results, since the worker threads |
| /// will already be ready to go even in the first iteration. But |
| /// this cost is minimal. |
| /// |
| /// Initialization of the global thread pool happens exactly |
| /// once. Once started, the configuration cannot be |
| /// changed. Therefore, if you call `build_global` a second time, it |
| /// will return an error. An `Ok` result indicates that this |
| /// is the first initialization of the thread pool. |
| pub fn build_global(self) -> Result<(), ThreadPoolBuildError> { |
| let registry = registry::init_global_registry(self)?; |
| registry.wait_until_primed(); |
| Ok(()) |
| } |
| } |
| |
| impl ThreadPoolBuilder { |
| /// Creates a scoped `ThreadPool` initialized using this configuration. |
| /// |
| /// This is a convenience function for building a pool using [`crossbeam::scope`] |
| /// to spawn threads in a [`spawn_handler`](#method.spawn_handler). |
| /// The threads in this pool will start by calling `wrapper`, which should |
| /// do initialization and continue by calling `ThreadBuilder::run()`. |
| /// |
| /// [`crossbeam::scope`]: https://docs.rs/crossbeam/0.7/crossbeam/fn.scope.html |
| /// |
| /// # Examples |
| /// |
| /// A scoped pool may be useful in combination with scoped thread-local variables. |
| /// |
| /// ``` |
| /// # use rayon_core as rayon; |
| /// |
| /// scoped_tls::scoped_thread_local!(static POOL_DATA: Vec<i32>); |
| /// |
| /// fn main() -> Result<(), rayon::ThreadPoolBuildError> { |
| /// let pool_data = vec![1, 2, 3]; |
| /// |
| /// // We haven't assigned any TLS data yet. |
| /// assert!(!POOL_DATA.is_set()); |
| /// |
| /// rayon::ThreadPoolBuilder::new() |
| /// .build_scoped( |
| /// // Borrow `pool_data` in TLS for each thread. |
| /// |thread| POOL_DATA.set(&pool_data, || thread.run()), |
| /// // Do some work that needs the TLS data. |
| /// |pool| pool.install(|| assert!(POOL_DATA.is_set())), |
| /// )?; |
| /// |
| /// // Once we've returned, `pool_data` is no longer borrowed. |
| /// drop(pool_data); |
| /// Ok(()) |
| /// } |
| /// ``` |
| pub fn build_scoped<W, F, R>(self, wrapper: W, with_pool: F) -> Result<R, ThreadPoolBuildError> |
| where |
| W: Fn(ThreadBuilder) + Sync, // expected to call `run()` |
| F: FnOnce(&ThreadPool) -> R, |
| { |
| let result = crossbeam_utils::thread::scope(|scope| { |
| let wrapper = &wrapper; |
| let pool = self |
| .spawn_handler(|thread| { |
| let mut builder = scope.builder(); |
| if let Some(name) = thread.name() { |
| builder = builder.name(name.to_string()); |
| } |
| if let Some(size) = thread.stack_size() { |
| builder = builder.stack_size(size); |
| } |
| builder.spawn(move |_| wrapper(thread))?; |
| Ok(()) |
| }) |
| .build()?; |
| Ok(with_pool(&pool)) |
| }); |
| |
| match result { |
| Ok(result) => result, |
| Err(err) => unwind::resume_unwinding(err), |
| } |
| } |
| } |
| |
| impl<S> ThreadPoolBuilder<S> { |
| /// Sets a custom function for spawning threads. |
| /// |
| /// Note that the threads will not exit until after the pool is dropped. It |
| /// is up to the caller to wait for thread termination if that is important |
| /// for any invariants. For instance, threads created in [`crossbeam::scope`] |
| /// will be joined before that scope returns, and this will block indefinitely |
| /// if the pool is leaked. Furthermore, the global thread pool doesn't terminate |
| /// until the entire process exits! |
| /// |
| /// [`crossbeam::scope`]: https://docs.rs/crossbeam/0.7/crossbeam/fn.scope.html |
| /// |
| /// # Examples |
| /// |
| /// A minimal spawn handler just needs to call `run()` from an independent thread. |
| /// |
| /// ``` |
| /// # use rayon_core as rayon; |
| /// fn main() -> Result<(), rayon::ThreadPoolBuildError> { |
| /// let pool = rayon::ThreadPoolBuilder::new() |
| /// .spawn_handler(|thread| { |
| /// std::thread::spawn(|| thread.run()); |
| /// Ok(()) |
| /// }) |
| /// .build()?; |
| /// |
| /// pool.install(|| println!("Hello from my custom thread!")); |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// The default spawn handler sets the name and stack size if given, and propagates |
| /// any errors from the thread builder. |
| /// |
| /// ``` |
| /// # use rayon_core as rayon; |
| /// fn main() -> Result<(), rayon::ThreadPoolBuildError> { |
| /// let pool = rayon::ThreadPoolBuilder::new() |
| /// .spawn_handler(|thread| { |
| /// let mut b = std::thread::Builder::new(); |
| /// if let Some(name) = thread.name() { |
| /// b = b.name(name.to_owned()); |
| /// } |
| /// if let Some(stack_size) = thread.stack_size() { |
| /// b = b.stack_size(stack_size); |
| /// } |
| /// b.spawn(|| thread.run())?; |
| /// Ok(()) |
| /// }) |
| /// .build()?; |
| /// |
| /// pool.install(|| println!("Hello from my fully custom thread!")); |
| /// Ok(()) |
| /// } |
| /// ``` |
| pub fn spawn_handler<F>(self, spawn: F) -> ThreadPoolBuilder<CustomSpawn<F>> |
| where |
| F: FnMut(ThreadBuilder) -> io::Result<()>, |
| { |
| ThreadPoolBuilder { |
| spawn_handler: CustomSpawn::new(spawn), |
| // ..self |
| num_threads: self.num_threads, |
| panic_handler: self.panic_handler, |
| get_thread_name: self.get_thread_name, |
| stack_size: self.stack_size, |
| start_handler: self.start_handler, |
| exit_handler: self.exit_handler, |
| breadth_first: self.breadth_first, |
| } |
| } |
| |
| /// Returns a reference to the current spawn handler. |
| fn get_spawn_handler(&mut self) -> &mut S { |
| &mut self.spawn_handler |
| } |
| |
| /// Get the number of threads that will be used for the thread |
| /// pool. See `num_threads()` for more information. |
| fn get_num_threads(&self) -> usize { |
| if self.num_threads > 0 { |
| self.num_threads |
| } else { |
| match env::var("RAYON_NUM_THREADS") |
| .ok() |
| .and_then(|s| usize::from_str(&s).ok()) |
| { |
| Some(x) if x > 0 => return x, |
| Some(x) if x == 0 => return num_cpus::get(), |
| _ => {} |
| } |
| |
| // Support for deprecated `RAYON_RS_NUM_CPUS`. |
| match env::var("RAYON_RS_NUM_CPUS") |
| .ok() |
| .and_then(|s| usize::from_str(&s).ok()) |
| { |
| Some(x) if x > 0 => x, |
| _ => num_cpus::get(), |
| } |
| } |
| } |
| |
| /// Get the thread name for the thread with the given index. |
| fn get_thread_name(&mut self, index: usize) -> Option<String> { |
| let f = self.get_thread_name.as_mut()?; |
| Some(f(index)) |
| } |
| |
| /// Sets a closure which takes a thread index and returns |
| /// the thread's name. |
| pub fn thread_name<F>(mut self, closure: F) -> Self |
| where |
| F: FnMut(usize) -> String + 'static, |
| { |
| self.get_thread_name = Some(Box::new(closure)); |
| self |
| } |
| |
| /// Sets the number of threads to be used in the rayon threadpool. |
| /// |
| /// If you specify a non-zero number of threads using this |
| /// function, then the resulting thread-pools are guaranteed to |
| /// start at most this number of threads. |
| /// |
| /// If `num_threads` is 0, or you do not call this function, then |
| /// the Rayon runtime will select the number of threads |
| /// automatically. At present, this is based on the |
| /// `RAYON_NUM_THREADS` environment variable (if set), |
| /// or the number of logical CPUs (otherwise). |
| /// In the future, however, the default behavior may |
| /// change to dynamically add or remove threads as needed. |
| /// |
| /// **Future compatibility warning:** Given the default behavior |
| /// may change in the future, if you wish to rely on a fixed |
| /// number of threads, you should use this function to specify |
| /// that number. To reproduce the current default behavior, you |
| /// may wish to use the [`num_cpus` |
| /// crate](https://crates.io/crates/num_cpus) to query the number |
| /// of CPUs dynamically. |
| /// |
| /// **Old environment variable:** `RAYON_NUM_THREADS` is a one-to-one |
| /// replacement of the now deprecated `RAYON_RS_NUM_CPUS` environment |
| /// variable. If both variables are specified, `RAYON_NUM_THREADS` will |
| /// be prefered. |
| pub fn num_threads(mut self, num_threads: usize) -> Self { |
| self.num_threads = num_threads; |
| self |
| } |
| |
| /// Returns a copy of the current panic handler. |
| fn take_panic_handler(&mut self) -> Option<Box<PanicHandler>> { |
| self.panic_handler.take() |
| } |
| |
| /// Normally, whenever Rayon catches a panic, it tries to |
| /// propagate it to someplace sensible, to try and reflect the |
| /// semantics of sequential execution. But in some cases, |
| /// particularly with the `spawn()` APIs, there is no |
| /// obvious place where we should propagate the panic to. |
| /// In that case, this panic handler is invoked. |
| /// |
| /// If no panic handler is set, the default is to abort the |
| /// process, under the principle that panics should not go |
| /// unobserved. |
| /// |
| /// If the panic handler itself panics, this will abort the |
| /// process. To prevent this, wrap the body of your panic handler |
| /// in a call to `std::panic::catch_unwind()`. |
| pub fn panic_handler<H>(mut self, panic_handler: H) -> Self |
| where |
| H: Fn(Box<dyn Any + Send>) + Send + Sync + 'static, |
| { |
| self.panic_handler = Some(Box::new(panic_handler)); |
| self |
| } |
| |
| /// Get the stack size of the worker threads |
| fn get_stack_size(&self) -> Option<usize> { |
| self.stack_size |
| } |
| |
| /// Sets the stack size of the worker threads |
| pub fn stack_size(mut self, stack_size: usize) -> Self { |
| self.stack_size = Some(stack_size); |
| self |
| } |
| |
| /// **(DEPRECATED)** Suggest to worker threads that they execute |
| /// spawned jobs in a "breadth-first" fashion. |
| /// |
| /// Typically, when a worker thread is idle or blocked, it will |
| /// attempt to execute the job from the *top* of its local deque of |
| /// work (i.e., the job most recently spawned). If this flag is set |
| /// to true, however, workers will prefer to execute in a |
| /// *breadth-first* fashion -- that is, they will search for jobs at |
| /// the *bottom* of their local deque. (At present, workers *always* |
| /// steal from the bottom of other worker's deques, regardless of |
| /// the setting of this flag.) |
| /// |
| /// If you think of the tasks as a tree, where a parent task |
| /// spawns its children in the tree, then this flag loosely |
| /// corresponds to doing a breadth-first traversal of the tree, |
| /// whereas the default would be to do a depth-first traversal. |
| /// |
| /// **Note that this is an "execution hint".** Rayon's task |
| /// execution is highly dynamic and the precise order in which |
| /// independent tasks are executed is not intended to be |
| /// guaranteed. |
| /// |
| /// This `breadth_first()` method is now deprecated per [RFC #1], |
| /// and in the future its effect may be removed. Consider using |
| /// [`scope_fifo()`] for a similar effect. |
| /// |
| /// [RFC #1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md |
| /// [`scope_fifo()`]: fn.scope_fifo.html |
| #[deprecated(note = "use `scope_fifo` and `spawn_fifo` for similar effect")] |
| pub fn breadth_first(mut self) -> Self { |
| self.breadth_first = true; |
| self |
| } |
| |
| fn get_breadth_first(&self) -> bool { |
| self.breadth_first |
| } |
| |
| /// Takes the current thread start callback, leaving `None`. |
| fn take_start_handler(&mut self) -> Option<Box<StartHandler>> { |
| self.start_handler.take() |
| } |
| |
| /// Sets a callback to be invoked on thread start. |
| /// |
| /// The closure is passed the index of the thread on which it is invoked. |
| /// Note that this same closure may be invoked multiple times in parallel. |
| /// If this closure panics, the panic will be passed to the panic handler. |
| /// If that handler returns, then startup will continue normally. |
| pub fn start_handler<H>(mut self, start_handler: H) -> Self |
| where |
| H: Fn(usize) + Send + Sync + 'static, |
| { |
| self.start_handler = Some(Box::new(start_handler)); |
| self |
| } |
| |
| /// Returns a current thread exit callback, leaving `None`. |
| fn take_exit_handler(&mut self) -> Option<Box<ExitHandler>> { |
| self.exit_handler.take() |
| } |
| |
| /// Sets a callback to be invoked on thread exit. |
| /// |
| /// The closure is passed the index of the thread on which it is invoked. |
| /// Note that this same closure may be invoked multiple times in parallel. |
| /// If this closure panics, the panic will be passed to the panic handler. |
| /// If that handler returns, then the thread will exit normally. |
| pub fn exit_handler<H>(mut self, exit_handler: H) -> Self |
| where |
| H: Fn(usize) + Send + Sync + 'static, |
| { |
| self.exit_handler = Some(Box::new(exit_handler)); |
| self |
| } |
| } |
| |
| #[allow(deprecated)] |
| impl Configuration { |
| /// Creates and return a valid rayon thread pool configuration, but does not initialize it. |
| pub fn new() -> Configuration { |
| Configuration { |
| builder: ThreadPoolBuilder::new(), |
| } |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::build`. |
| pub fn build(self) -> Result<ThreadPool, Box<dyn Error + 'static>> { |
| self.builder.build().map_err(Box::from) |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::thread_name`. |
| pub fn thread_name<F>(mut self, closure: F) -> Self |
| where |
| F: FnMut(usize) -> String + 'static, |
| { |
| self.builder = self.builder.thread_name(closure); |
| self |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::num_threads`. |
| pub fn num_threads(mut self, num_threads: usize) -> Configuration { |
| self.builder = self.builder.num_threads(num_threads); |
| self |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::panic_handler`. |
| pub fn panic_handler<H>(mut self, panic_handler: H) -> Configuration |
| where |
| H: Fn(Box<dyn Any + Send>) + Send + Sync + 'static, |
| { |
| self.builder = self.builder.panic_handler(panic_handler); |
| self |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::stack_size`. |
| pub fn stack_size(mut self, stack_size: usize) -> Self { |
| self.builder = self.builder.stack_size(stack_size); |
| self |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::breadth_first`. |
| pub fn breadth_first(mut self) -> Self { |
| self.builder = self.builder.breadth_first(); |
| self |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::start_handler`. |
| pub fn start_handler<H>(mut self, start_handler: H) -> Configuration |
| where |
| H: Fn(usize) + Send + Sync + 'static, |
| { |
| self.builder = self.builder.start_handler(start_handler); |
| self |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::exit_handler`. |
| pub fn exit_handler<H>(mut self, exit_handler: H) -> Configuration |
| where |
| H: Fn(usize) + Send + Sync + 'static, |
| { |
| self.builder = self.builder.exit_handler(exit_handler); |
| self |
| } |
| |
| /// Returns a ThreadPoolBuilder with identical parameters. |
| fn into_builder(self) -> ThreadPoolBuilder { |
| self.builder |
| } |
| } |
| |
| impl ThreadPoolBuildError { |
| fn new(kind: ErrorKind) -> ThreadPoolBuildError { |
| ThreadPoolBuildError { kind } |
| } |
| } |
| |
| const GLOBAL_POOL_ALREADY_INITIALIZED: &str = |
| "The global thread pool has already been initialized."; |
| |
| impl Error for ThreadPoolBuildError { |
| #[allow(deprecated)] |
| fn description(&self) -> &str { |
| match self.kind { |
| ErrorKind::GlobalPoolAlreadyInitialized => GLOBAL_POOL_ALREADY_INITIALIZED, |
| ErrorKind::IOError(ref e) => e.description(), |
| } |
| } |
| |
| fn source(&self) -> Option<&(dyn Error + 'static)> { |
| match &self.kind { |
| ErrorKind::GlobalPoolAlreadyInitialized => None, |
| ErrorKind::IOError(e) => Some(e), |
| } |
| } |
| } |
| |
| impl fmt::Display for ThreadPoolBuildError { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| match &self.kind { |
| ErrorKind::GlobalPoolAlreadyInitialized => GLOBAL_POOL_ALREADY_INITIALIZED.fmt(f), |
| ErrorKind::IOError(e) => e.fmt(f), |
| } |
| } |
| } |
| |
| /// Deprecated in favor of `ThreadPoolBuilder::build_global`. |
| #[deprecated(note = "use `ThreadPoolBuilder::build_global`")] |
| #[allow(deprecated)] |
| pub fn initialize(config: Configuration) -> Result<(), Box<dyn Error>> { |
| config.into_builder().build_global().map_err(Box::from) |
| } |
| |
| impl<S> fmt::Debug for ThreadPoolBuilder<S> { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let ThreadPoolBuilder { |
| ref num_threads, |
| ref get_thread_name, |
| ref panic_handler, |
| ref stack_size, |
| ref start_handler, |
| ref exit_handler, |
| spawn_handler: _, |
| ref breadth_first, |
| } = *self; |
| |
| // Just print `Some(<closure>)` or `None` to the debug |
| // output. |
| struct ClosurePlaceholder; |
| impl fmt::Debug for ClosurePlaceholder { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.write_str("<closure>") |
| } |
| } |
| let get_thread_name = get_thread_name.as_ref().map(|_| ClosurePlaceholder); |
| let panic_handler = panic_handler.as_ref().map(|_| ClosurePlaceholder); |
| let start_handler = start_handler.as_ref().map(|_| ClosurePlaceholder); |
| let exit_handler = exit_handler.as_ref().map(|_| ClosurePlaceholder); |
| |
| f.debug_struct("ThreadPoolBuilder") |
| .field("num_threads", num_threads) |
| .field("get_thread_name", &get_thread_name) |
| .field("panic_handler", &panic_handler) |
| .field("stack_size", &stack_size) |
| .field("start_handler", &start_handler) |
| .field("exit_handler", &exit_handler) |
| .field("breadth_first", &breadth_first) |
| .finish() |
| } |
| } |
| |
| #[allow(deprecated)] |
| impl Default for Configuration { |
| fn default() -> Self { |
| Configuration { |
| builder: Default::default(), |
| } |
| } |
| } |
| |
| #[allow(deprecated)] |
| impl fmt::Debug for Configuration { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| self.builder.fmt(f) |
| } |
| } |
| |
| /// Provides the calling context to a closure called by `join_context`. |
| #[derive(Debug)] |
| pub struct FnContext { |
| migrated: bool, |
| |
| /// disable `Send` and `Sync`, just for a little future-proofing. |
| _marker: PhantomData<*mut ()>, |
| } |
| |
| impl FnContext { |
| #[inline] |
| fn new(migrated: bool) -> Self { |
| FnContext { |
| migrated, |
| _marker: PhantomData, |
| } |
| } |
| } |
| |
| impl FnContext { |
| /// Returns `true` if the closure was called from a different thread |
| /// than it was provided from. |
| #[inline] |
| pub fn migrated(&self) -> bool { |
| self.migrated |
| } |
| } |