//! 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. #![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 broadcast; 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::broadcast::{broadcast, spawn_broadcast, BroadcastContext}; 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 maximum number of threads that Rayon supports in a single thread-pool. /// /// If a higher thread count is requested by calling `ThreadPoolBuilder::num_threads` or by setting /// the `RAYON_NUM_THREADS` environment variable, then it will be reduced to this maximum. /// /// The value may vary between different targets, and is subject to change in new Rayon versions. pub fn max_num_threads() -> usize { // We are limited by the bits available in the sleep counter's `AtomicUsize`. crate::sleep::THREADS_MAX } /// 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 { /// 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>, /// Closure to compute the name of a thread. get_thread_name: Option String>>, /// The stack size for the created worker threads stack_size: Option, /// Closure invoked on worker thread start. start_handler: Option>, /// Closure invoked on worker thread exit. exit_handler: Option>, /// 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`")] #[derive(Default)] 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) + 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 ThreadPoolBuilder where S: ThreadSpawn, { /// Creates a new `ThreadPool` initialized using this configuration. pub fn build(self) -> Result { 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.8/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); /// /// 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(self, wrapper: W, with_pool: F) -> Result 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 ThreadPoolBuilder { /// 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.8/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(()) /// } /// ``` /// /// This can also be used for a pool of scoped threads like [`crossbeam::scope`], /// or [`std::thread::scope`] introduced in Rust 1.63, which is encapsulated in /// [`build_scoped`](#method.build_scoped). /// /// [`std::thread::scope`]: https://doc.rust-lang.org/std/thread/fn.scope.html /// /// ``` /// # use rayon_core as rayon; /// fn main() -> Result<(), rayon::ThreadPoolBuildError> { /// std::thread::scope(|scope| { /// let pool = rayon::ThreadPoolBuilder::new() /// .spawn_handler(|thread| { /// let mut builder = std::thread::Builder::new(); /// 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_scoped(scope, || { /// // Add any scoped initialization here, then run! /// thread.run() /// })?; /// Ok(()) /// }) /// .build()?; /// /// pool.install(|| println!("Hello from my custom scoped thread!")); /// Ok(()) /// }) /// } /// ``` pub fn spawn_handler(self, spawn: F) -> ThreadPoolBuilder> 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 { 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(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 preferred. 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> { 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(mut self, panic_handler: H) -> Self where H: Fn(Box) + 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 { 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 workers' 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> { 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(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> { 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(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> { self.builder.build().map_err(Box::from) } /// Deprecated in favor of `ThreadPoolBuilder::thread_name`. pub fn thread_name(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(mut self, panic_handler: H) -> Configuration where H: Fn(Box) + 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(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(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> { config.into_builder().build_global().map_err(Box::from) } impl fmt::Debug for ThreadPoolBuilder { 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()` 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("") } } 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 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 } }