| //! Free functions. | |
| use super::builder::Builder; | |
| use super::current::current; | |
| use super::join_handle::JoinHandle; | |
| use crate::mem::forget; | |
| use crate::num::NonZero; | |
| use crate::sys::thread as imp; | |
| use crate::time::{Duration, Instant}; | |
| use crate::{io, panicking}; | |
| /// Spawns a new thread, returning a [`JoinHandle`] for it. | |
| /// | |
| /// The join handle provides a [`join`] method that can be used to join the spawned | |
| /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing | |
| /// the argument given to [`panic!`]. | |
| /// | |
| /// If the join handle is dropped, the spawned thread will implicitly be *detached*. | |
| /// In this case, the spawned thread may no longer be joined. | |
| /// (It is the responsibility of the program to either eventually join threads it | |
| /// creates or detach them; otherwise, a resource leak will result.) | |
| /// | |
| /// This function creates a thread with the default parameters of [`Builder`]. | |
| /// To specify the new thread's stack size or the name, use [`Builder::spawn`]. | |
| /// | |
| /// As you can see in the signature of `spawn` there are two constraints on | |
| /// both the closure given to `spawn` and its return value, let's explain them: | |
| /// | |
| /// - The `'static` constraint means that the closure and its return value | |
| /// must have a lifetime of the whole program execution. The reason for this | |
| /// is that threads can outlive the lifetime they have been created in. | |
| /// | |
| /// Indeed if the thread, and by extension its return value, can outlive their | |
| /// caller, we need to make sure that they will be valid afterwards, and since | |
| /// we *can't* know when it will return we need to have them valid as long as | |
| /// possible, that is until the end of the program, hence the `'static` | |
| /// lifetime. | |
| /// - The [`Send`] constraint is because the closure will need to be passed | |
| /// *by value* from the thread where it is spawned to the new thread. Its | |
| /// return value will need to be passed from the new thread to the thread | |
| /// where it is `join`ed. | |
| /// As a reminder, the [`Send`] marker trait expresses that it is safe to be | |
| /// passed from thread to thread. [`Sync`] expresses that it is safe to have a | |
| /// reference be passed from thread to thread. | |
| /// | |
| /// # Panics | |
| /// | |
| /// Panics if the OS fails to create a thread; use [`Builder::spawn`] | |
| /// to recover from such errors. | |
| /// | |
| /// # Examples | |
| /// | |
| /// Creating a thread. | |
| /// | |
| /// ``` | |
| /// use std::thread; | |
| /// | |
| /// let handler = thread::spawn(|| { | |
| /// // thread code | |
| /// }); | |
| /// | |
| /// handler.join().unwrap(); | |
| /// ``` | |
| /// | |
| /// As mentioned in the module documentation, threads are usually made to | |
| /// communicate using [`channels`], here is how it usually looks. | |
| /// | |
| /// This example also shows how to use `move`, in order to give ownership | |
| /// of values to a thread. | |
| /// | |
| /// ``` | |
| /// use std::thread; | |
| /// use std::sync::mpsc::channel; | |
| /// | |
| /// let (tx, rx) = channel(); | |
| /// | |
| /// let sender = thread::spawn(move || { | |
| /// tx.send("Hello, thread".to_owned()) | |
| /// .expect("Unable to send on channel"); | |
| /// }); | |
| /// | |
| /// let receiver = thread::spawn(move || { | |
| /// let value = rx.recv().expect("Unable to receive from channel"); | |
| /// println!("{value}"); | |
| /// }); | |
| /// | |
| /// sender.join().expect("The sender thread has panicked"); | |
| /// receiver.join().expect("The receiver thread has panicked"); | |
| /// ``` | |
| /// | |
| /// A thread can also return a value through its [`JoinHandle`], you can use | |
| /// this to make asynchronous computations (futures might be more appropriate | |
| /// though). | |
| /// | |
| /// ``` | |
| /// use std::thread; | |
| /// | |
| /// let computation = thread::spawn(|| { | |
| /// // Some expensive computation. | |
| /// 42 | |
| /// }); | |
| /// | |
| /// let result = computation.join().unwrap(); | |
| /// println!("{result}"); | |
| /// ``` | |
| /// | |
| /// # Notes | |
| /// | |
| /// This function has the same minimal guarantee regarding "foreign" unwinding operations (e.g. | |
| /// an exception thrown from C++ code, or a `panic!` in Rust code compiled or linked with a | |
| /// different runtime) as [`catch_unwind`]; namely, if the thread created with `thread::spawn` | |
| /// unwinds all the way to the root with such an exception, one of two behaviors are possible, | |
| /// and it is unspecified which will occur: | |
| /// | |
| /// * The process aborts. | |
| /// * The process does not abort, and [`join`] will return a `Result::Err` | |
| /// containing an opaque type. | |
| /// | |
| /// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html | |
| /// [`channels`]: crate::sync::mpsc | |
| /// [`join`]: JoinHandle::join | |
| /// [`Err`]: crate::result::Result::Err | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces | |
| pub fn spawn<F, T>(f: F) -> JoinHandle<T> | |
| where | |
| F: FnOnce() -> T, | |
| F: Send + 'static, | |
| T: Send + 'static, | |
| { | |
| Builder::new().spawn(f).expect("failed to spawn thread") | |
| } | |
| /// Cooperatively gives up a timeslice to the OS scheduler. | |
| /// | |
| /// This calls the underlying OS scheduler's yield primitive, signaling | |
| /// that the calling thread is willing to give up its remaining timeslice | |
| /// so that the OS may schedule other threads on the CPU. | |
| /// | |
| /// A drawback of yielding in a loop is that if the OS does not have any | |
| /// other ready threads to run on the current CPU, the thread will effectively | |
| /// busy-wait, which wastes CPU time and energy. | |
| /// | |
| /// Therefore, when waiting for events of interest, a programmer's first | |
| /// choice should be to use synchronization devices such as [`channel`]s, | |
| /// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are | |
| /// implemented in a blocking manner, giving up the CPU until the event | |
| /// of interest has occurred which avoids repeated yielding. | |
| /// | |
| /// `yield_now` should thus be used only rarely, mostly in situations where | |
| /// repeated polling is required because there is no other suitable way to | |
| /// learn when an event of interest has occurred. | |
| /// | |
| /// # Examples | |
| /// | |
| /// ``` | |
| /// use std::thread; | |
| /// | |
| /// thread::yield_now(); | |
| /// ``` | |
| /// | |
| /// [`channel`]: crate::sync::mpsc | |
| /// [`join`]: JoinHandle::join | |
| /// [`Condvar`]: crate::sync::Condvar | |
| /// [`Mutex`]: crate::sync::Mutex | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| pub fn yield_now() { | |
| imp::yield_now() | |
| } | |
| /// Determines whether the current thread is panicking. | |
| /// | |
| /// This returns `true` both when the thread is unwinding due to a panic, | |
| /// or executing a panic hook. Note that the latter case will still happen | |
| /// when `panic=abort` is set. | |
| /// | |
| /// A common use of this feature is to poison shared resources when writing | |
| /// unsafe code, by checking `panicking` when the `drop` is called. | |
| /// | |
| /// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] | |
| /// already poison themselves when a thread panics while holding the lock. | |
| /// | |
| /// This can also be used in multithreaded applications, in order to send a | |
| /// message to other threads warning that a thread has panicked (e.g., for | |
| /// monitoring purposes). | |
| /// | |
| /// # Examples | |
| /// | |
| /// ```should_panic | |
| /// use std::thread; | |
| /// | |
| /// struct SomeStruct; | |
| /// | |
| /// impl Drop for SomeStruct { | |
| /// fn drop(&mut self) { | |
| /// if thread::panicking() { | |
| /// println!("dropped while unwinding"); | |
| /// } else { | |
| /// println!("dropped while not unwinding"); | |
| /// } | |
| /// } | |
| /// } | |
| /// | |
| /// { | |
| /// print!("a: "); | |
| /// let a = SomeStruct; | |
| /// } | |
| /// | |
| /// { | |
| /// print!("b: "); | |
| /// let b = SomeStruct; | |
| /// panic!() | |
| /// } | |
| /// ``` | |
| /// | |
| /// [Mutex]: crate::sync::Mutex | |
| #[inline] | |
| #[must_use] | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| pub fn panicking() -> bool { | |
| panicking::panicking() | |
| } | |
| /// Uses [`sleep`]. | |
| /// | |
| /// Puts the current thread to sleep for at least the specified amount of time. | |
| /// | |
| /// The thread may sleep longer than the duration specified due to scheduling | |
| /// specifics or platform-dependent functionality. It will never sleep less. | |
| /// | |
| /// This function is blocking, and should not be used in `async` functions. | |
| /// | |
| /// # Platform-specific behavior | |
| /// | |
| /// On Unix platforms, the underlying syscall may be interrupted by a | |
| /// spurious wakeup or signal handler. To ensure the sleep occurs for at least | |
| /// the specified duration, this function may invoke that system call multiple | |
| /// times. | |
| /// | |
| /// # Examples | |
| /// | |
| /// ```no_run | |
| /// use std::thread; | |
| /// | |
| /// // Let's sleep for 2 seconds: | |
| /// thread::sleep_ms(2000); | |
| /// ``` | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| #[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")] | |
| pub fn sleep_ms(ms: u32) { | |
| sleep(Duration::from_millis(ms as u64)) | |
| } | |
| /// Puts the current thread to sleep for at least the specified amount of time. | |
| /// | |
| /// The thread may sleep longer than the duration specified due to scheduling | |
| /// specifics or platform-dependent functionality. It will never sleep less. | |
| /// | |
| /// This function is blocking, and should not be used in `async` functions. | |
| /// | |
| /// # Platform-specific behavior | |
| /// | |
| /// On Unix platforms, the underlying syscall may be interrupted by a | |
| /// spurious wakeup or signal handler. To ensure the sleep occurs for at least | |
| /// the specified duration, this function may invoke that system call multiple | |
| /// times. | |
| /// Platforms which do not support nanosecond precision for sleeping will | |
| /// have `dur` rounded up to the nearest granularity of time they can sleep for. | |
| /// | |
| /// Currently, specifying a zero duration on Unix platforms returns immediately | |
| /// without invoking the underlying [`nanosleep`] syscall, whereas on Windows | |
| /// platforms the underlying [`Sleep`] syscall is always invoked. | |
| /// If the intention is to yield the current time-slice you may want to use | |
| /// [`yield_now`] instead. | |
| /// | |
| /// [`nanosleep`]: https://linux.die.net/man/2/nanosleep | |
| /// [`Sleep`]: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep | |
| /// | |
| /// # Examples | |
| /// | |
| /// ```no_run | |
| /// use std::{thread, time}; | |
| /// | |
| /// let ten_millis = time::Duration::from_millis(10); | |
| /// let now = time::Instant::now(); | |
| /// | |
| /// thread::sleep(ten_millis); | |
| /// | |
| /// assert!(now.elapsed() >= ten_millis); | |
| /// ``` | |
| #[stable(feature = "thread_sleep", since = "1.4.0")] | |
| pub fn sleep(dur: Duration) { | |
| imp::sleep(dur) | |
| } | |
| /// Puts the current thread to sleep until the specified deadline has passed. | |
| /// | |
| /// If the deadline has already passed at the time this function is called, it | |
| /// will return immediately. Note that the thread may still be asleep after the | |
| /// deadline specified due to scheduling specifics or platform-dependent | |
| /// functionality. It will never wake before. | |
| /// | |
| /// This function is blocking, and should not be used in `async` functions. | |
| /// | |
| /// # Platform-specific behavior | |
| /// | |
| /// In most cases this function will call an OS specific function. Where that | |
| /// is not supported [`sleep`] is used. Those platforms are referred to as other | |
| /// in the table below. | |
| /// | |
| /// # Underlying System calls | |
| /// | |
| /// The following system calls are [currently] being used: | |
| /// | |
| /// | Platform | System call | | |
| /// |-----------|----------------------------------------------------------------------| | |
| /// | Linux | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | BSD except OpenBSD | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | Android | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | Solaris | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | Illumos | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | Dragonfly | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | Hurd | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | Vxworks | [`clock_nanosleep`] (Monotonic Clock) | | |
| /// | Apple | `mach_wait_until` | | |
| /// | Fuchsia | [`zx_nanosleep`] | | |
| /// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | | |
| /// | |
| /// [currently]: crate::io#platform-specific-behavior | |
| /// [`clock_nanosleep`]: https://linux.die.net/man/3/clock_nanosleep | |
| /// [`zx_nanosleep`]: https://fuchsia.dev/reference/syscalls/nanosleep | |
| /// | |
| /// **Disclaimer:** These system calls might change over time. | |
| /// | |
| /// # Examples | |
| /// | |
| /// A simple game loop that limits the game to 60 frames per second. | |
| /// | |
| /// ```no_run | |
| /// #![feature(thread_sleep_until)] | |
| /// # use std::time::{Duration, Instant}; | |
| /// # use std::thread; | |
| /// # | |
| /// # fn update() {} | |
| /// # fn render() {} | |
| /// # | |
| /// let max_fps = 60.0; | |
| /// let frame_time = Duration::from_secs_f32(1.0/max_fps); | |
| /// let mut next_frame = Instant::now(); | |
| /// loop { | |
| /// thread::sleep_until(next_frame); | |
| /// next_frame += frame_time; | |
| /// update(); | |
| /// render(); | |
| /// } | |
| /// ``` | |
| /// | |
| /// A slow API we must not call too fast and which takes a few | |
| /// tries before succeeding. By using `sleep_until` the time the | |
| /// API call takes does not influence when we retry or when we give up | |
| /// | |
| /// ```no_run | |
| /// #![feature(thread_sleep_until)] | |
| /// # use std::time::{Duration, Instant}; | |
| /// # use std::thread; | |
| /// # | |
| /// # enum Status { | |
| /// # Ready(usize), | |
| /// # Waiting, | |
| /// # } | |
| /// # fn slow_web_api_call() -> Status { Status::Ready(42) } | |
| /// # | |
| /// # const MAX_DURATION: Duration = Duration::from_secs(10); | |
| /// # | |
| /// # fn try_api_call() -> Result<usize, ()> { | |
| /// let deadline = Instant::now() + MAX_DURATION; | |
| /// let delay = Duration::from_millis(250); | |
| /// let mut next_attempt = Instant::now(); | |
| /// loop { | |
| /// if Instant::now() > deadline { | |
| /// break Err(()); | |
| /// } | |
| /// if let Status::Ready(data) = slow_web_api_call() { | |
| /// break Ok(data); | |
| /// } | |
| /// | |
| /// next_attempt = deadline.min(next_attempt + delay); | |
| /// thread::sleep_until(next_attempt); | |
| /// } | |
| /// # } | |
| /// # let _data = try_api_call(); | |
| /// ``` | |
| #[unstable(feature = "thread_sleep_until", issue = "113752")] | |
| pub fn sleep_until(deadline: Instant) { | |
| imp::sleep_until(deadline) | |
| } | |
| /// Used to ensure that `park` and `park_timeout` do not unwind, as that can | |
| /// cause undefined behavior if not handled correctly (see #102398 for context). | |
| struct PanicGuard; | |
| impl Drop for PanicGuard { | |
| fn drop(&mut self) { | |
| rtabort!("an irrecoverable error occurred while synchronizing threads") | |
| } | |
| } | |
| /// Blocks unless or until the current thread's token is made available. | |
| /// | |
| /// A call to `park` does not guarantee that the thread will remain parked | |
| /// forever, and callers should be prepared for this possibility. However, | |
| /// it is guaranteed that this function will not panic (it may abort the | |
| /// process if the implementation encounters some rare errors). | |
| /// | |
| /// # `park` and `unpark` | |
| /// | |
| /// Every thread is equipped with some basic low-level blocking support, via the | |
| /// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] | |
| /// method. [`park`] blocks the current thread, which can then be resumed from | |
| /// another thread by calling the [`unpark`] method on the blocked thread's | |
| /// handle. | |
| /// | |
| /// Conceptually, each [`Thread`] handle has an associated token, which is | |
| /// initially not present: | |
| /// | |
| /// * The [`thread::park`][`park`] function blocks the current thread unless or | |
| /// until the token is available for its thread handle, at which point it | |
| /// atomically consumes the token. It may also return *spuriously*, without | |
| /// consuming the token. [`thread::park_timeout`] does the same, but allows | |
| /// specifying a maximum time to block the thread for. | |
| /// | |
| /// * The [`unpark`] method on a [`Thread`] atomically makes the token available | |
| /// if it wasn't already. Because the token can be held by a thread even if it is currently not | |
| /// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. | |
| /// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens | |
| /// after all `park` that may be done by other data structures! | |
| /// | |
| /// The API is typically used by acquiring a handle to the current thread, placing that handle in a | |
| /// shared data structure so that other threads can find it, and then `park`ing in a loop. When some | |
| /// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point | |
| /// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it | |
| /// will be woken up properly. | |
| /// | |
| /// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread | |
| /// without first establishing that it is about to be `park`ing within your code, that `unpark` may | |
| /// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means | |
| /// you must not call unknown code between setting up for parking and calling `park`; for instance, | |
| /// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a | |
| /// deadlock. | |
| /// | |
| /// The motivation for this design is twofold: | |
| /// | |
| /// * It avoids the need to allocate mutexes and condvars when building new | |
| /// synchronization primitives; the threads already provide basic | |
| /// blocking/signaling. | |
| /// | |
| /// * It can be implemented very efficiently on many platforms. | |
| /// | |
| /// # Memory Ordering | |
| /// | |
| /// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory | |
| /// operations performed before a call to `unpark` are made visible to the thread that | |
| /// consumes the token and returns from `park`. Note that all `park` and `unpark` | |
| /// operations for a given thread form a total order and _all_ prior `unpark` operations | |
| /// synchronize-with `park`. | |
| /// | |
| /// In atomic ordering terms, `unpark` performs a `Release` operation and `park` | |
| /// performs the corresponding `Acquire` operation. Calls to `unpark` for the same | |
| /// thread form a [release sequence]. | |
| /// | |
| /// Note that being unblocked does not imply a call was made to `unpark`, because | |
| /// wakeups can also be spurious. For example, a valid, but inefficient, | |
| /// implementation could have `park` and `unpark` return immediately without doing anything, | |
| /// making *all* wakeups spurious. | |
| /// | |
| /// # Examples | |
| /// | |
| /// ``` | |
| /// use std::thread; | |
| /// use std::sync::atomic::{Ordering, AtomicBool}; | |
| /// use std::time::Duration; | |
| /// | |
| /// static QUEUED: AtomicBool = AtomicBool::new(false); | |
| /// static FLAG: AtomicBool = AtomicBool::new(false); | |
| /// | |
| /// let parked_thread = thread::spawn(move || { | |
| /// println!("Thread spawned"); | |
| /// // Signal that we are going to `park`. Between this store and our `park`, there may | |
| /// // be no other `park`, or else that `park` could consume our `unpark` token! | |
| /// QUEUED.store(true, Ordering::Release); | |
| /// // We want to wait until the flag is set. We *could* just spin, but using | |
| /// // park/unpark is more efficient. | |
| /// while !FLAG.load(Ordering::Acquire) { | |
| /// // We can *not* use `println!` here since that could use thread parking internally. | |
| /// thread::park(); | |
| /// // We *could* get here spuriously, i.e., way before the 10ms below are over! | |
| /// // But that is no problem, we are in a loop until the flag is set anyway. | |
| /// } | |
| /// println!("Flag received"); | |
| /// }); | |
| /// | |
| /// // Let some time pass for the thread to be spawned. | |
| /// thread::sleep(Duration::from_millis(10)); | |
| /// | |
| /// // Ensure the thread is about to park. | |
| /// // This is crucial! It guarantees that the `unpark` below is not consumed | |
| /// // by some other code in the parked thread (e.g. inside `println!`). | |
| /// while !QUEUED.load(Ordering::Acquire) { | |
| /// // Spinning is of course inefficient; in practice, this would more likely be | |
| /// // a dequeue where we have no work to do if there's nobody queued. | |
| /// std::hint::spin_loop(); | |
| /// } | |
| /// | |
| /// // Set the flag, and let the thread wake up. | |
| /// // There is no race condition here: if `unpark` | |
| /// // happens first, `park` will return immediately. | |
| /// // There is also no other `park` that could consume this token, | |
| /// // since we waited until the other thread got queued. | |
| /// // Hence there is no risk of a deadlock. | |
| /// FLAG.store(true, Ordering::Release); | |
| /// println!("Unpark the thread"); | |
| /// parked_thread.thread().unpark(); | |
| /// | |
| /// parked_thread.join().unwrap(); | |
| /// ``` | |
| /// | |
| /// [`Thread`]: super::Thread | |
| /// [`unpark`]: super::Thread::unpark | |
| /// [`thread::park_timeout`]: park_timeout | |
| /// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| pub fn park() { | |
| let guard = PanicGuard; | |
| // SAFETY: park_timeout is called on the parker owned by this thread. | |
| unsafe { | |
| current().park(); | |
| } | |
| // No panic occurred, do not abort. | |
| forget(guard); | |
| } | |
| /// Uses [`park_timeout`]. | |
| /// | |
| /// Blocks unless or until the current thread's token is made available or | |
| /// the specified duration has been reached (may wake spuriously). | |
| /// | |
| /// The semantics of this function are equivalent to [`park`] except | |
| /// that the thread will be blocked for roughly no longer than `dur`. This | |
| /// method should not be used for precise timing due to anomalies such as | |
| /// preemption or platform differences that might not cause the maximum | |
| /// amount of time waited to be precisely `ms` long. | |
| /// | |
| /// See the [park documentation][`park`] for more detail. | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| #[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")] | |
| pub fn park_timeout_ms(ms: u32) { | |
| park_timeout(Duration::from_millis(ms as u64)) | |
| } | |
| /// Blocks unless or until the current thread's token is made available or | |
| /// the specified duration has been reached (may wake spuriously). | |
| /// | |
| /// The semantics of this function are equivalent to [`park`][park] except | |
| /// that the thread will be blocked for roughly no longer than `dur`. This | |
| /// method should not be used for precise timing due to anomalies such as | |
| /// preemption or platform differences that might not cause the maximum | |
| /// amount of time waited to be precisely `dur` long. | |
| /// | |
| /// See the [park documentation][park] for more details. | |
| /// | |
| /// # Platform-specific behavior | |
| /// | |
| /// Platforms which do not support nanosecond precision for sleeping will have | |
| /// `dur` rounded up to the nearest granularity of time they can sleep for. | |
| /// | |
| /// # Examples | |
| /// | |
| /// Waiting for the complete expiration of the timeout: | |
| /// | |
| /// ```rust,no_run | |
| /// use std::thread::park_timeout; | |
| /// use std::time::{Instant, Duration}; | |
| /// | |
| /// let timeout = Duration::from_secs(2); | |
| /// let beginning_park = Instant::now(); | |
| /// | |
| /// let mut timeout_remaining = timeout; | |
| /// loop { | |
| /// park_timeout(timeout_remaining); | |
| /// let elapsed = beginning_park.elapsed(); | |
| /// if elapsed >= timeout { | |
| /// break; | |
| /// } | |
| /// println!("restarting park_timeout after {elapsed:?}"); | |
| /// timeout_remaining = timeout - elapsed; | |
| /// } | |
| /// ``` | |
| #[stable(feature = "park_timeout", since = "1.4.0")] | |
| pub fn park_timeout(dur: Duration) { | |
| let guard = PanicGuard; | |
| // SAFETY: park_timeout is called on a handle owned by this thread. | |
| unsafe { | |
| current().park_timeout(dur); | |
| } | |
| // No panic occurred, do not abort. | |
| forget(guard); | |
| } | |
| /// Returns an estimate of the default amount of parallelism a program should use. | |
| /// | |
| /// Parallelism is a resource. A given machine provides a certain capacity for | |
| /// parallelism, i.e., a bound on the number of computations it can perform | |
| /// simultaneously. This number often corresponds to the amount of CPUs a | |
| /// computer has, but it may diverge in various cases. | |
| /// | |
| /// Host environments such as VMs or container orchestrators may want to | |
| /// restrict the amount of parallelism made available to programs in them. This | |
| /// is often done to limit the potential impact of (unintentionally) | |
| /// resource-intensive programs on other programs running on the same machine. | |
| /// | |
| /// # Limitations | |
| /// | |
| /// The purpose of this API is to provide an easy and portable way to query | |
| /// the default amount of parallelism the program should use. Among other things it | |
| /// does not expose information on NUMA regions, does not account for | |
| /// differences in (co)processor capabilities or current system load, | |
| /// and will not modify the program's global state in order to more accurately | |
| /// query the amount of available parallelism. | |
| /// | |
| /// Where both fixed steady-state and burst limits are available the steady-state | |
| /// capacity will be used to ensure more predictable latencies. | |
| /// | |
| /// Resource limits can be changed during the runtime of a program, therefore the value is | |
| /// not cached and instead recomputed every time this function is called. It should not be | |
| /// called from hot code. | |
| /// | |
| /// The value returned by this function should be considered a simplified | |
| /// approximation of the actual amount of parallelism available at any given | |
| /// time. To get a more detailed or precise overview of the amount of | |
| /// parallelism available to the program, you may wish to use | |
| /// platform-specific APIs as well. The following platform limitations currently | |
| /// apply to `available_parallelism`: | |
| /// | |
| /// On Windows: | |
| /// - It may undercount the amount of parallelism available on systems with more | |
| /// than 64 logical CPUs, because it reports only the logical CPUs in one | |
| /// processor group. Before Windows 11 and Windows Server 2022, a process was by | |
| /// default confined to a single processor group, so this count reflected the CPUs | |
| /// it could use without explicitly opting into other groups. Starting with Windows | |
| /// 11 and Windows Server 2022, a process and its threads have affinities that by | |
| /// default span all processor groups, so on systems with more than 64 logical CPUs | |
| /// this may report fewer CPUs than are available to the program. | |
| /// - It may overcount the amount of parallelism available on systems limited by | |
| /// process-wide affinity masks, or job object limitations. | |
| /// | |
| /// On Linux: | |
| /// - It may overcount the amount of parallelism available when limited by a | |
| /// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be | |
| /// queried, e.g. due to sandboxing. | |
| /// - It may undercount the amount of parallelism if the current thread's affinity mask | |
| /// does not reflect the process' cpuset, e.g. due to pinned threads. | |
| /// - If the process is in a cgroup v1 cpu controller, this may need to | |
| /// scan mountpoints to find the corresponding cgroup v1 controller, | |
| /// which may take time on systems with large numbers of mountpoints. | |
| /// (This does not apply to cgroup v2, or to processes not in a | |
| /// cgroup.) | |
| /// - It does not attempt to take `ulimit` into account. If there is a limit set on the number of | |
| /// threads, `available_parallelism` cannot know how much of that limit a Rust program should | |
| /// take, or know in a reliable and race-free way how much of that limit is already taken. | |
| /// | |
| /// On all targets: | |
| /// - It may overcount the amount of parallelism available when running in a VM | |
| /// with CPU usage limits (e.g. an overcommitted host). | |
| /// | |
| /// # Errors | |
| /// | |
| /// This function will, but is not limited to, return errors in the following | |
| /// cases: | |
| /// | |
| /// - If the amount of parallelism is not known for the target platform. | |
| /// - If the program lacks permission to query the amount of parallelism made | |
| /// available to it. | |
| /// | |
| /// # Examples | |
| /// | |
| /// ``` | |
| /// use std::thread; | |
| /// | |
| /// if let Ok(count) = thread::available_parallelism() { | |
| /// assert!(count.get() >= 1_usize); | |
| /// } | |
| /// ``` | |
| #[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. | |
| #[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`. | |
| #[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality. | |
| #[stable(feature = "available_parallelism", since = "1.59.0")] | |
| pub fn available_parallelism() -> io::Result<NonZero<usize>> { | |
| imp::available_parallelism() | |
| } |