Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
267 changes: 267 additions & 0 deletions library/std/src/thread/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
use super::join_handle::JoinHandle;
use super::lifecycle::spawn_unchecked;
use crate::io;

/// Thread factory, which can be used in order to configure the properties of
/// a new thread.
///
/// Methods can be chained on it in order to configure it.
///
/// The two configurations available are:
///
/// - [`name`]: specifies an [associated name for the thread][naming-threads]
/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size]
///
/// The [`spawn`] method will take ownership of the builder and create an
/// [`io::Result`] to the thread handle with the given configuration.
///
/// The [`thread::spawn`] free function uses a `Builder` with default
/// configuration and [`unwrap`]s its return value.
///
/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want
/// to recover from a failure to launch a thread, indeed the free function will
/// panic where the `Builder` method will return a [`io::Result`].
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// let builder = thread::Builder::new();
///
/// let handler = builder.spawn(|| {
/// // thread code
/// }).unwrap();
///
/// handler.join().unwrap();
/// ```
///
/// [`stack_size`]: Builder::stack_size
/// [`name`]: Builder::name
/// [`spawn`]: Builder::spawn
/// [`thread::spawn`]: super::spawn
/// [`io::Result`]: crate::io::Result
/// [`unwrap`]: crate::result::Result::unwrap
/// [naming-threads]: ./index.html#naming-threads
/// [stack-size]: ./index.html#stack-size
#[must_use = "must eventually spawn the thread"]
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Debug)]
pub struct Builder {
/// A name for the thread-to-be, for identification in panic messages
pub(super) name: Option<String>,
/// The size of the stack for the spawned thread in bytes
pub(super) stack_size: Option<usize>,
/// Skip running and inheriting the thread spawn hooks
pub(super) no_hooks: bool,
}

impl Builder {
/// Generates the base configuration for spawning a thread, from which
/// configuration methods can be chained.
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// let builder = thread::Builder::new()
/// .name("foo".into())
/// .stack_size(32 * 1024);
///
/// let handler = builder.spawn(|| {
/// // thread code
/// }).unwrap();
///
/// handler.join().unwrap();
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> Builder {
Builder { name: None, stack_size: None, no_hooks: false }
}

/// Names the thread-to-be. Currently the name is used for identification
/// only in panic messages.
///
/// The name must not contain null bytes (`\0`).
///
/// For more information about named threads, see
/// [this module-level documentation][naming-threads].
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// let builder = thread::Builder::new()
/// .name("foo".into());
///
/// let handler = builder.spawn(|| {
/// assert_eq!(thread::current().name(), Some("foo"))
/// }).unwrap();
///
/// handler.join().unwrap();
/// ```
///
/// [naming-threads]: ./index.html#naming-threads
#[stable(feature = "rust1", since = "1.0.0")]
pub fn name(mut self, name: String) -> Builder {
self.name = Some(name);
self
}

/// Sets the size of the stack (in bytes) for the new thread.
///
/// The actual stack size may be greater than this value if
/// the platform specifies a minimal stack size.
///
/// For more information about the stack size for threads, see
/// [this module-level documentation][stack-size].
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// let builder = thread::Builder::new().stack_size(32 * 1024);
/// ```
///
/// [stack-size]: ./index.html#stack-size
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stack_size(mut self, size: usize) -> Builder {
self.stack_size = Some(size);
self
}

/// Disables running and inheriting [spawn hooks].
///
/// Use this if the parent thread is in no way relevant for the child thread.
/// For example, when lazily spawning threads for a thread pool.
///
/// [spawn hooks]: super::add_spawn_hook
#[unstable(feature = "thread_spawn_hook", issue = "132951")]
pub fn no_hooks(mut self) -> Builder {
self.no_hooks = true;
self
}

/// Spawns a new thread by taking ownership of the `Builder`, and returns an
/// [`io::Result`] to its [`JoinHandle`].
///
/// The spawned thread may outlive the caller (unless the caller thread
/// is the main thread; the whole process is terminated when the main
/// thread finishes). The join handle can be used to block on
/// termination of the spawned thread, including recovering its panics.
///
/// For a more complete documentation see [`thread::spawn`].
///
/// # Errors
///
/// Unlike the [`spawn`] free function, this method yields an
/// [`io::Result`] to capture any failure to create the thread at
/// the OS level.
///
/// [`io::Result`]: crate::io::Result
///
/// # Panics
///
/// Panics if a thread name was set and it contained null bytes.
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// let builder = thread::Builder::new();
///
/// let handler = builder.spawn(|| {
/// // thread code
/// }).unwrap();
///
/// handler.join().unwrap();
/// ```
///
/// [`thread::spawn`]: super::spawn
/// [`spawn`]: super::spawn
#[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>(self, f: F) -> io::Result<JoinHandle<T>>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
unsafe { self.spawn_unchecked(f) }
}

/// Spawns a new thread without any lifetime restrictions by taking ownership
/// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`].
///
/// The spawned thread may outlive the caller (unless the caller thread
/// is the main thread; the whole process is terminated when the main
/// thread finishes). The join handle can be used to block on
/// termination of the spawned thread, including recovering its panics.
///
/// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`],
/// except for the relaxed lifetime bounds, which render it unsafe.
/// For a more complete documentation see [`thread::spawn`].
///
/// # Errors
///
/// Unlike the [`spawn`] free function, this method yields an
/// [`io::Result`] to capture any failure to create the thread at
/// the OS level.
///
/// # Panics
///
/// Panics if a thread name was set and it contained null bytes.
///
/// # Safety
///
/// The caller has to ensure that the spawned thread does not outlive any
/// references in the supplied thread closure and its return type.
/// This can be guaranteed in two ways:
///
/// - ensure that [`join`][`JoinHandle::join`] is called before any referenced
/// data is dropped
/// - use only types with `'static` lifetime bounds, i.e., those with no or only
/// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`]
/// and [`thread::spawn`] enforce this property statically)
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// let builder = thread::Builder::new();
///
/// let x = 1;
/// let thread_x = &x;
///
/// let handler = unsafe {
/// builder.spawn_unchecked(move || {
/// println!("x = {}", *thread_x);
/// }).unwrap()
/// };
///
/// // caller has to ensure `join()` is called, otherwise
/// // it is possible to access freed memory if `x` gets
/// // dropped before the thread closure is executed!
/// handler.join().unwrap();
/// ```
///
/// [`io::Result`]: crate::io::Result
/// [`thread::spawn`]: super::spawn
/// [`spawn`]: super::spawn
#[stable(feature = "thread_spawn_unchecked", since = "1.82.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
where
F: FnOnce() -> T,
F: Send,
T: Send,
{
let Builder { name, stack_size, no_hooks } = self;
Ok(JoinHandle(unsafe { spawn_unchecked(name, stack_size, no_hooks, None, f) }?))
}
}
37 changes: 35 additions & 2 deletions library/std/src/thread/current.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use super::{Thread, ThreadId, imp};
use super::id::ThreadId;
use super::main_thread;
use super::thread::Thread;
use crate::mem::ManuallyDrop;
use crate::ptr;
use crate::sys::thread as imp;
use crate::sys::thread_local::local_pointer;

const NONE: *mut () = ptr::null_mut();
Expand Down Expand Up @@ -184,7 +187,7 @@ pub(crate) fn current_os_id() -> u64 {

/// Gets a reference to the handle of the thread that invokes it, if the handle
/// has been initialized.
pub(super) fn try_with_current<F, R>(f: F) -> R
fn try_with_current<F, R>(f: F) -> R
where
F: FnOnce(Option<&Thread>) -> R,
{
Expand All @@ -202,6 +205,36 @@ where
}
}

/// Run a function with the current thread's name.
///
/// Modulo thread local accesses, this function is safe to call from signal
/// handlers and in similar circumstances where allocations are not possible.
pub(crate) fn with_current_name<F, R>(f: F) -> R
where
F: FnOnce(Option<&str>) -> R,
{
try_with_current(|thread| {
let name = if let Some(thread) = thread {
// If there is a current thread handle, try to use the name stored
// there.
thread.name()
} else if let Some(main) = main_thread::get()
&& let Some(id) = id::get()
&& id == main
{
// The main thread doesn't always have a thread handle, we must
// identify it through its ID instead. The checks are ordered so
// that the current ID is only loaded if it is actually needed,
// since loading it from TLS might need multiple expensive accesses.
Some("main")
} else {
None
};

f(name)
})
}

/// Gets a handle to the thread that invokes it. If the handle stored in thread-
/// local storage was already destroyed, this creates a new unnamed temporary
/// handle to allow thread parking in nearly all situations.
Expand Down
Loading
Loading