Add more from the std

This commit is contained in:
2026-03-18 14:59:16 +01:00
parent 9413fba265
commit 51780b3a78
35 changed files with 9176 additions and 1270 deletions

View File

@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
hashbrown = "0.16"
os-std-macros = { path = "../os-std-macros" }
shared = { path = "../shared", features = ["user"] }
io = { path = "../io", features = ["alloc"] }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
//! Unordered containers, implemented as hash-tables
pub mod map;
pub mod set;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
use core::marker::PhantomData;
pub struct HashMap<K, V, T> {
_phantom: PhantomData<(K, V, T)>,
}
pub use alloc_crate::collections;

5
crates/std/src/env.rs Normal file
View File

@@ -0,0 +1,5 @@
use crate::ffi::OsString;
pub fn var_os(s: &str) -> Option<OsString> {
None
}

View File

@@ -9,7 +9,7 @@ pub mod copy;
pub mod cursor;
pub mod impls;
pub mod pipe;
// pub mod stdio;
pub mod stdio;
pub mod prelude;
pub mod util;
@@ -18,7 +18,9 @@ use crate::ops::{Deref, DerefMut};
use crate::{cmp, fmt, slice, str, sys};
#[unstable(feature = "read_buf", issue = "78485")]
pub use core::io::{BorrowedBuf, BorrowedCursor};
pub use cursor::Cursor;
use core::slice::memchr;
pub use stdio::try_set_output_capture;
use crate::fs::File;
use io::IoBase;

File diff suppressed because it is too large Load Diff

View File

@@ -267,6 +267,11 @@ pub use core::{
stringify, trace_macros,
};
#[rustc_std_internal_symbol]
pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -> u32 {
todo!()
}
pub mod ffi;
pub mod hash;
pub mod io;
@@ -280,6 +285,10 @@ pub mod process;
pub mod rt;
pub mod alloc;
pub mod bstr;
pub mod collections;
pub mod env;
pub mod panic;
pub mod panicking;
pub mod sync;
pub mod sys;
pub mod thread;

534
crates/std/src/panic.rs Normal file
View File

@@ -0,0 +1,534 @@
//! Panic support in the standard library.
#![stable(feature = "std_panic", since = "1.9.0")]
use crate::any::Any;
use crate::sync::atomic::{Atomic, AtomicU8, Ordering};
use crate::sync::{Condvar, Mutex, RwLock};
use crate::thread::Result;
use crate::{collections, fmt, panicking};
#[stable(feature = "panic_hooks", since = "1.10.0")]
#[deprecated(
since = "1.82.0",
note = "use `PanicHookInfo` instead",
suggestion = "std::panic::PanicHookInfo"
)]
/// A struct providing information about a panic.
///
/// `PanicInfo` has been renamed to [`PanicHookInfo`] to avoid confusion with
/// [`core::panic::PanicInfo`].
pub type PanicInfo<'a> = PanicHookInfo<'a>;
/// A struct providing information about a panic.
///
/// `PanicHookInfo` structure is passed to a panic hook set by the [`set_hook`] function.
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// println!("panic occurred: {panic_info}");
/// }));
///
/// panic!("critical system failure");
/// ```
///
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
#[stable(feature = "panic_hook_info", since = "1.81.0")]
#[derive(Debug)]
pub struct PanicHookInfo<'a> {
payload: &'a (dyn Any + Send),
location: &'a Location<'a>,
can_unwind: bool,
force_no_backtrace: bool,
}
impl<'a> PanicHookInfo<'a> {
#[inline]
pub(crate) fn new(
location: &'a Location<'a>,
payload: &'a (dyn Any + Send),
can_unwind: bool,
force_no_backtrace: bool,
) -> Self {
PanicHookInfo { payload, location, can_unwind, force_no_backtrace }
}
/// Returns the payload associated with the panic.
///
/// This will commonly, but not always, be a `&'static str` or [`String`].
/// If you only care about such payloads, use [`payload_as_str`] instead.
///
/// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a
/// panic payload of type `&'static str` or `String`.
///
/// Only an invocation of [`panic_any`]
/// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string)
/// can result in a panic payload other than a `&'static str` or `String`.
///
/// [`String`]: ../../std/string/struct.String.html
/// [`payload_as_str`]: PanicHookInfo::payload_as_str
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
/// println!("panic occurred: {s:?}");
/// } else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
/// println!("panic occurred: {s:?}");
/// } else {
/// println!("panic occurred");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[must_use]
#[inline]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn payload(&self) -> &(dyn Any + Send) {
self.payload
}
/// Returns the payload associated with the panic, if it is a string.
///
/// This returns the payload if it is of type `&'static str` or `String`.
///
/// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a
/// panic payload where `payload_as_str` returns `Some`.
///
/// Only an invocation of [`panic_any`]
/// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string)
/// can result in a panic payload where `payload_as_str` returns `None`.
///
/// # Example
///
/// ```should_panic
/// std::panic::set_hook(Box::new(|panic_info| {
/// if let Some(s) = panic_info.payload_as_str() {
/// println!("panic occurred: {s:?}");
/// } else {
/// println!("panic occurred");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[must_use]
#[inline]
#[stable(feature = "panic_payload_as_str", since = "1.91.0")]
pub fn payload_as_str(&self) -> Option<&str> {
if let Some(s) = self.payload.downcast_ref::<&str>() {
Some(s)
} else if let Some(s) = self.payload.downcast_ref::<String>() {
Some(s)
} else {
None
}
}
/// Returns information about the location from which the panic originated,
/// if available.
///
/// This method will currently always return [`Some`], but this may change
/// in future versions.
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
/// if let Some(location) = panic_info.location() {
/// println!("panic occurred in file '{}' at line {}",
/// location.file(),
/// location.line(),
/// );
/// } else {
/// println!("panic occurred but can't get location information...");
/// }
/// }));
///
/// panic!("Normal panic");
/// ```
#[must_use]
#[inline]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn location(&self) -> Option<&Location<'_>> {
// NOTE: If this is changed to sometimes return None,
// deal with that case in std::panicking::default_hook and core::panicking::panic_fmt.
Some(&self.location)
}
/// Returns whether the panic handler is allowed to unwind the stack from
/// the point where the panic occurred.
///
/// This is true for most kinds of panics with the exception of panics
/// caused by trying to unwind out of a `Drop` implementation or a function
/// whose ABI does not support unwinding.
///
/// It is safe for a panic handler to unwind even when this function returns
/// false, however this will simply cause the panic handler to be called
/// again.
#[must_use]
#[inline]
#[unstable(feature = "panic_can_unwind", issue = "92988")]
pub fn can_unwind(&self) -> bool {
self.can_unwind
}
#[unstable(
feature = "panic_internals",
reason = "internal details of the implementation of the `panic!` and related macros",
issue = "none"
)]
#[doc(hidden)]
#[inline]
pub fn force_no_backtrace(&self) -> bool {
self.force_no_backtrace
}
}
#[stable(feature = "panic_hook_display", since = "1.26.0")]
impl fmt::Display for PanicHookInfo<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("panicked at ")?;
self.location.fmt(formatter)?;
if let Some(payload) = self.payload_as_str() {
formatter.write_str(":\n")?;
formatter.write_str(payload)?;
}
Ok(())
}
}
#[doc(hidden)]
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
#[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)]
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")]
#[rustc_macro_transparency = "semiopaque"]
pub macro panic_2015 {
() => ({
$crate::rt::begin_panic("explicit panic")
}),
($msg:expr $(,)?) => ({
$crate::rt::begin_panic($msg);
}),
// Special-case the single-argument case for const_panic.
("{}", $arg:expr $(,)?) => ({
$crate::rt::panic_display(&$arg);
}),
($fmt:expr, $($arg:tt)+) => ({
// Semicolon to prevent temporaries inside the formatting machinery from
// being considered alive in the caller after the panic_fmt call.
$crate::rt::panic_fmt($crate::const_format_args!($fmt, $($arg)+));
}),
}
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub use core::panic::Location;
#[doc(hidden)]
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
pub use core::panic::panic_2021;
#[stable(feature = "catch_unwind", since = "1.9.0")]
pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
#[unstable(feature = "panic_update_hook", issue = "92649")]
pub use crate::panicking::update_hook;
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub use crate::panicking::{set_hook, take_hook};
/// Panics the current thread with the given message as the panic payload.
///
/// The message can be of any (`Any + Send`) type, not just strings.
///
/// The message is wrapped in a `Box<'static + Any + Send>`, which can be
/// accessed later using [`PanicHookInfo::payload`].
///
/// See the [`panic!`] macro for more information about panicking.
#[stable(feature = "panic_any", since = "1.51.0")]
#[inline]
#[track_caller]
#[cfg_attr(not(test), rustc_diagnostic_item = "panic_any")]
pub fn panic_any<M: 'static + Any + Send>(msg: M) -> ! {
crate::panicking::begin_panic(msg);
}
#[stable(feature = "catch_unwind", since = "1.9.0")]
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
#[stable(feature = "catch_unwind", since = "1.9.0")]
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
#[stable(feature = "catch_unwind", since = "1.9.0")]
impl UnwindSafe for Condvar {}
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
impl RefUnwindSafe for Condvar {}
// https://github.com/rust-lang/rust/issues/62301
#[stable(feature = "hashbrown", since = "1.36.0")]
impl<K, V, S> UnwindSafe for collections::HashMap<K, V, S>
where
K: UnwindSafe,
V: UnwindSafe,
S: UnwindSafe,
{
}
#[unstable(feature = "abort_unwind", issue = "130338")]
pub use core::panic::abort_unwind;
/// Invokes a closure, capturing the cause of an unwinding panic if one occurs.
///
/// This function will return `Ok` with the closure's result if the closure does
/// not panic, and will return `Err(cause)` if the closure panics. The `cause`
/// returned is the object with which panic was originally invoked.
///
/// Rust functions that are expected to be called from foreign code that does
/// not support unwinding (such as C compiled with `-fno-exceptions`) should be
/// defined using `extern "C"`, which ensures that if the Rust code panics, it
/// is automatically caught and the process is aborted. If this is the desired
/// behavior, it is not necessary to use `catch_unwind` explicitly. This
/// function should instead be used when more graceful error-handling is needed.
///
/// It is **not** recommended to use this function for a general try/catch
/// mechanism. The [`Result`] type is more appropriate to use for functions that
/// can fail on a regular basis. Additionally, this function is not guaranteed
/// to catch all panics, see the "Notes" section below.
///
/// The closure provided is required to adhere to the [`UnwindSafe`] trait to
/// ensure that all captured variables are safe to cross this boundary. The
/// purpose of this bound is to encode the concept of [exception safety][rfc] in
/// the type system. Most usage of this function should not need to worry about
/// this bound as programs are naturally unwind safe without `unsafe` code. If
/// it becomes a problem the [`AssertUnwindSafe`] wrapper struct can be used to
/// quickly assert that the usage here is indeed unwind safe.
///
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
///
/// # Notes
///
/// This function **might not catch all Rust panics**. A Rust panic is not
/// always implemented via unwinding, but can be implemented by aborting the
/// process as well. This function *only* catches unwinding panics, not those
/// that abort the process.
///
/// If a custom panic hook has been set, it will be invoked before the panic is
/// caught, before unwinding.
///
/// Although unwinding into Rust code with a foreign exception (e.g. an
/// exception thrown from C++ code, or a `panic!` in Rust code compiled or
/// linked with a different runtime) via an appropriate ABI (e.g. `"C-unwind"`)
/// is permitted, catching such an exception using this function will have one
/// of two behaviors, and it is unspecified which will occur:
///
/// * The process aborts, after executing all destructors of `f` and the
/// functions it called.
/// * The function returns a `Result::Err` containing an opaque type.
///
/// Finally, be **careful in how you drop the result of this function**. If it
/// is `Err`, it contains the panic payload, and dropping that may in turn
/// panic!
///
/// # Examples
///
/// ```
/// use std::panic;
///
/// let result = panic::catch_unwind(|| {
/// println!("hello!");
/// });
/// assert!(result.is_ok());
///
/// let result = panic::catch_unwind(|| {
/// panic!("oh no!");
/// });
/// assert!(result.is_err());
/// ```
#[stable(feature = "catch_unwind", since = "1.9.0")]
pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
unsafe { panicking::catch_unwind(f) }
}
/// Triggers a panic without invoking the panic hook.
///
/// This is designed to be used in conjunction with [`catch_unwind`] to, for
/// example, carry a panic across a layer of C code.
///
/// # Notes
///
/// Note that panics in Rust are not always implemented via unwinding, but they
/// may be implemented by aborting the process. If this function is called when
/// panics are implemented this way then this function will abort the process,
/// not trigger an unwind.
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// let result = panic::catch_unwind(|| {
/// if 1 != 2 {
/// panic!("oh no!");
/// }
/// });
///
/// if let Err(err) = result {
/// panic::resume_unwind(err);
/// }
/// ```
#[stable(feature = "resume_unwind", since = "1.9.0")]
pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! {
panicking::resume_unwind(payload)
}
/// Makes all future panics abort directly without running the panic hook or unwinding.
///
/// There is no way to undo this; the effect lasts until the process exits or
/// execs (or the equivalent).
///
/// # Use after fork
///
/// This function is particularly useful for calling after `libc::fork`. After `fork`, in a
/// multithreaded program it is (on many platforms) not safe to call the allocator. It is also
/// generally highly undesirable for an unwind to unwind past the `fork`, because that results in
/// the unwind propagating to code that was only ever expecting to run in the parent.
///
/// `panic::always_abort()` helps avoid both of these. It directly avoids any further unwinding,
/// and if there is a panic, the abort will occur without allocating provided that the arguments to
/// panic can be formatted without allocating.
///
/// Examples
///
/// ```no_run
/// #![feature(panic_always_abort)]
/// use std::panic;
///
/// panic::always_abort();
///
/// let _ = panic::catch_unwind(|| {
/// panic!("inside the catch");
/// });
///
/// // We will have aborted already, due to the panic.
/// unreachable!();
/// ```
#[unstable(feature = "panic_always_abort", issue = "84438")]
pub fn always_abort() {
crate::panicking::panic_count::set_always_abort();
}
/// The configuration for whether and how the default panic hook will capture
/// and display the backtrace.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[unstable(feature = "panic_backtrace_config", issue = "93346")]
#[non_exhaustive]
pub enum BacktraceStyle {
/// Prints a terser backtrace which ideally only contains relevant
/// information.
Short,
/// Prints a backtrace with all possible information.
Full,
/// Disable collecting and displaying backtraces.
Off,
}
impl BacktraceStyle {
pub(crate) fn full() -> Option<Self> {
if cfg!(feature = "backtrace") { Some(BacktraceStyle::Full) } else { None }
}
fn as_u8(self) -> u8 {
match self {
BacktraceStyle::Short => 1,
BacktraceStyle::Full => 2,
BacktraceStyle::Off => 3,
}
}
fn from_u8(s: u8) -> Option<Self> {
match s {
1 => Some(BacktraceStyle::Short),
2 => Some(BacktraceStyle::Full),
3 => Some(BacktraceStyle::Off),
_ => None,
}
}
}
// Tracks whether we should/can capture a backtrace, and how we should display
// that backtrace.
//
// Internally stores equivalent of an Option<BacktraceStyle>.
static SHOULD_CAPTURE: Atomic<u8> = AtomicU8::new(0);
/// Configures whether the default panic hook will capture and display a
/// backtrace.
///
/// The default value for this setting may be set by the `RUST_BACKTRACE`
/// environment variable; see the details in [`get_backtrace_style`].
#[unstable(feature = "panic_backtrace_config", issue = "93346")]
pub fn set_backtrace_style(style: BacktraceStyle) {
if cfg!(feature = "backtrace") {
// If the `backtrace` feature of this crate is enabled, set the backtrace style.
SHOULD_CAPTURE.store(style.as_u8(), Ordering::Relaxed);
}
}
/// Checks whether the standard library's panic hook will capture and print a
/// backtrace.
///
/// This function will, if a backtrace style has not been set via
/// [`set_backtrace_style`], read the environment variable `RUST_BACKTRACE` to
/// determine a default value for the backtrace formatting:
///
/// The first call to `get_backtrace_style` may read the `RUST_BACKTRACE`
/// environment variable if `set_backtrace_style` has not been called to
/// override the default value. After a call to `set_backtrace_style` or
/// `get_backtrace_style`, any changes to `RUST_BACKTRACE` will have no effect.
///
/// `RUST_BACKTRACE` is read according to these rules:
///
/// * `0` for `BacktraceStyle::Off`
/// * `full` for `BacktraceStyle::Full`
/// * `1` for `BacktraceStyle::Short`
/// * Other values are currently `BacktraceStyle::Short`, but this may change in
/// the future
///
/// Returns `None` if backtraces aren't currently supported.
#[unstable(feature = "panic_backtrace_config", issue = "93346")]
pub fn get_backtrace_style() -> Option<BacktraceStyle> {
if !cfg!(feature = "backtrace") {
// If the `backtrace` feature of this crate isn't enabled quickly return
// `Unsupported` so this can be constant propagated all over the place
// to optimize away callers.
return None;
}
let current = SHOULD_CAPTURE.load(Ordering::Relaxed);
if let Some(style) = BacktraceStyle::from_u8(current) {
return Some(style);
}
let format = match crate::env::var_os("RUST_BACKTRACE") {
Some(x) if &x == "0" => BacktraceStyle::Off,
Some(x) if &x == "full" => BacktraceStyle::Full,
Some(_) => BacktraceStyle::Short,
None if crate::sys::backtrace::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full,
None => BacktraceStyle::Off,
};
match SHOULD_CAPTURE.compare_exchange(0, format.as_u8(), Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => Some(format),
Err(new) => BacktraceStyle::from_u8(new),
}
}

894
crates/std/src/panicking.rs Normal file
View File

@@ -0,0 +1,894 @@
//! Implementation of various bits and pieces of the `panic!` macro and
//! associated runtime pieces.
//!
//! Specifically, this module contains the implementation of:
//!
//! * Panic hooks
//! * Executing a panic up to doing the actual implementation
//! * Shims around "try"
#![deny(unsafe_op_in_unsafe_fn)]
use core::panic::{Location, PanicPayload};
// make sure to use the stderr output configured
// by libtest in the real copy of std
#[cfg(test)]
use realstd::io::try_set_output_capture;
use crate::any::Any;
#[cfg(not(test))]
use crate::io::try_set_output_capture;
use crate::mem::{self, ManuallyDrop};
use crate::panic::{BacktraceStyle, PanicHookInfo};
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
use crate::sync::nonpoison::RwLock;
use crate::sys::backtrace;
use crate::sys::stdio::panic_output;
use crate::{fmt, intrinsics, process, thread};
// This forces codegen of the function called by panic!() inside the std crate, rather than in
// downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing
// complete removal of panic from generated IR. Since begin_panic is inline(never), it's only
// codegen'd once per crate-graph so this pushes that to std rather than our codegen test crates.
//
// (See https://github.com/rust-lang/rust/pull/123244 for more info on why).
//
// If this is causing problems we can also modify those codegen tests to use a crate type like
// cdylib which doesn't export "Rust" symbols to downstream linkage units.
#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
#[doc(hidden)]
#[allow(dead_code)]
#[used(compiler)]
pub static EMPTY_PANIC: fn(&'static str) -> ! =
begin_panic::<&'static str> as fn(&'static str) -> !;
// Binary interface to the panic runtime that the standard library depends on.
//
// The standard library is tagged with `#![needs_panic_runtime]` (introduced in
// RFC 1513) to indicate that it requires some other crate tagged with
// `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to
// implement these symbols (with the same signatures) so we can get matched up
// to them.
//
// One day this may look a little less ad-hoc with the compiler helping out to
// hook up these functions, but it is not this day!
#[allow(improper_ctypes)]
unsafe extern "C" {
#[rustc_std_internal_symbol]
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
}
unsafe extern "Rust" {
/// `PanicPayload` lazily performs allocation only when needed (this avoids
/// allocations when using the "abort" panic runtime).
#[rustc_std_internal_symbol]
fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32;
}
/// This function is called by the panic runtime if FFI code catches a Rust
/// panic but doesn't rethrow it. We don't support this case since it messes
/// with our panic count.
#[cfg(not(test))]
#[rustc_std_internal_symbol]
extern "C" fn __rust_drop_panic() -> ! {
rtabort!("Rust panics must be rethrown");
}
/// This function is called by the panic runtime if it catches an exception
/// object which does not correspond to a Rust panic.
#[cfg(not(test))]
#[rustc_std_internal_symbol]
extern "C" fn __rust_foreign_exception() -> ! {
rtabort!("Rust cannot catch foreign exceptions");
}
#[derive(Default)]
enum Hook {
#[default]
Default,
Custom(Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>),
}
impl Hook {
#[inline]
fn into_box(self) -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
match self {
Hook::Default => Box::new(default_hook),
Hook::Custom(hook) => hook,
}
}
}
static HOOK: RwLock<Hook> = RwLock::new(Hook::Default);
/// Registers a custom panic hook, replacing the previously registered hook.
///
/// The panic hook is invoked when a thread panics, but before the panic runtime
/// is invoked. As such, the hook will run with both the aborting and unwinding
/// runtimes.
///
/// The default hook, which is registered at startup, prints a message to standard error and
/// generates a backtrace if requested. This behavior can be customized using the `set_hook` function.
/// The current hook can be retrieved while reinstating the default hook with the [`take_hook`]
/// function.
///
/// [`take_hook`]: ./fn.take_hook.html
///
/// The hook is provided with a `PanicHookInfo` struct which contains information
/// about the origin of the panic, including the payload passed to `panic!` and
/// the source code location from which the panic originated.
///
/// The panic hook is a global resource.
///
/// # Panics
///
/// Panics if called from a panicking thread.
///
/// # Examples
///
/// The following will print "Custom panic hook":
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|_| {
/// println!("Custom panic hook");
/// }));
///
/// panic!("Normal panic");
/// ```
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn set_hook(hook: Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>) {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
// Drop the old hook after changing the hook to avoid deadlocking if its
// destructor panics.
drop(HOOK.replace(Hook::Custom(hook)));
}
/// Unregisters the current panic hook and returns it, registering the default hook
/// in its place.
///
/// *See also the function [`set_hook`].*
///
/// [`set_hook`]: ./fn.set_hook.html
///
/// If the default hook is registered it will be returned, but remain registered.
///
/// # Panics
///
/// Panics if called from a panicking thread.
///
/// # Examples
///
/// The following will print "Normal panic":
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|_| {
/// println!("Custom panic hook");
/// }));
///
/// let _ = panic::take_hook();
///
/// panic!("Normal panic");
/// ```
#[must_use]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub fn take_hook() -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
HOOK.replace(Hook::Default).into_box()
}
/// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with
/// a new panic handler that does something and then executes the old handler.
///
/// [`take_hook`]: ./fn.take_hook.html
/// [`set_hook`]: ./fn.set_hook.html
///
/// # Panics
///
/// Panics if called from a panicking thread.
///
/// # Examples
///
/// The following will print the custom message, and then the normal output of panic.
///
/// ```should_panic
/// #![feature(panic_update_hook)]
/// use std::panic;
///
/// // Equivalent to
/// // let prev = panic::take_hook();
/// // panic::set_hook(Box::new(move |info| {
/// // println!("...");
/// // prev(info);
/// // }));
/// panic::update_hook(move |prev, info| {
/// println!("Print custom message and execute panic handler as usual");
/// prev(info);
/// });
///
/// panic!("Custom and then normal");
/// ```
#[unstable(feature = "panic_update_hook", issue = "92649")]
pub fn update_hook<F>(hook_fn: F)
where
F: Fn(&(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), &PanicHookInfo<'_>)
+ Sync
+ Send
+ 'static,
{
if thread::panicking() {
panic!("cannot modify the panic hook from a panicking thread");
}
let mut hook = HOOK.write();
let prev = mem::take(&mut *hook).into_box();
*hook = Hook::Custom(Box::new(move |info| hook_fn(&prev, info)));
}
/// The default panic handler.
#[optimize(size)]
fn default_hook(info: &PanicHookInfo<'_>) {
// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
let backtrace = if info.force_no_backtrace() {
None
} else if panic_count::get_count() >= 2 {
BacktraceStyle::full()
} else {
crate::panic::get_backtrace_style()
};
// The current implementation always returns `Some`.
let location = info.location().unwrap();
let msg = payload_as_str(info.payload());
let write = #[optimize(size)]
|err: &mut dyn crate::io::Write| {
// Use a lock to prevent mixed output in multithreading context.
// Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
let mut lock = backtrace::lock();
thread::with_current_name(|name| {
let name = name.unwrap_or("<unnamed>");
let tid = thread::current_os_id();
// Try to write the panic message to a buffer first to prevent other concurrent outputs
// interleaving with it.
let mut buffer = [0u8; 512];
let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
let write_msg = |dst: &mut dyn crate::io::Write| {
// We add a newline to ensure the panic message appears at the start of a line.
writeln!(dst, "\nthread '{name}' ({tid}) panicked at {location}:\n{msg}")
};
if write_msg(&mut cursor).is_ok() {
let pos = cursor.position() as usize;
let _ = err.write_all(&buffer[0..pos]);
} else {
// The message did not fit into the buffer, write it directly instead.
let _ = write_msg(err);
};
});
static FIRST_PANIC: Atomic<bool> = AtomicBool::new(true);
match backtrace {
Some(BacktraceStyle::Short) => {
todo!()
}
Some(BacktraceStyle::Full) => {
todo!()
}
Some(BacktraceStyle::Off) => {
if FIRST_PANIC.swap(false, Ordering::Relaxed) {
let _ = writeln!(
err,
"note: run with `RUST_BACKTRACE=1` environment variable to display a \
backtrace"
);
if cfg!(miri) {
let _ = writeln!(
err,
"note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \
for the environment variable to have an effect"
);
}
}
}
// If backtraces aren't supported or are forced-off, do nothing.
None => {}
}
};
if let Ok(Some(local)) = try_set_output_capture(None) {
write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()));
try_set_output_capture(Some(local)).ok();
} else if let Some(mut out) = panic_output() {
write(&mut out);
}
}
#[cfg(not(test))]
#[doc(hidden)]
#[cfg(panic = "immediate-abort")]
#[unstable(feature = "update_panic_count", issue = "none")]
pub mod panic_count {
/// A reason for forcing an immediate abort on panic.
#[derive(Debug)]
pub enum MustAbort {
AlwaysAbort,
PanicInHook,
}
#[inline]
pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
None
}
#[inline]
pub fn finished_panic_hook() {}
#[inline]
pub fn decrease() {}
#[inline]
pub fn set_always_abort() {}
// Disregards ALWAYS_ABORT_FLAG
#[inline]
#[must_use]
pub fn get_count() -> usize {
0
}
#[must_use]
#[inline]
pub fn count_is_zero() -> bool {
true
}
}
#[cfg(not(test))]
#[doc(hidden)]
#[cfg(not(panic = "immediate-abort"))]
#[unstable(feature = "update_panic_count", issue = "none")]
pub mod panic_count {
use crate::cell::Cell;
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1);
/// A reason for forcing an immediate abort on panic.
#[derive(Debug)]
pub enum MustAbort {
AlwaysAbort,
PanicInHook,
}
// Panic count for the current thread and whether a panic hook is currently
// being executed..
thread_local! {
static LOCAL_PANIC_COUNT: Cell<(usize, bool)> = const { Cell::new((0, false)) }
}
// Sum of panic counts from all threads. The purpose of this is to have
// a fast path in `count_is_zero` (which is used by `panicking`). In any particular
// thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero,
// then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before
// and after increase and decrease, but not necessarily during their execution.
//
// Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
// records whether panic::always_abort() has been called. This can only be
// set, never cleared.
// panic::always_abort() is usually called to prevent memory allocations done by
// the panic handling in the child created by `libc::fork`.
// Memory allocations performed in a child created with `libc::fork` are undefined
// behavior in most operating systems.
// Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory
// allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is
// sufficient because a child process will always have exactly one thread only.
// See also #85261 for details.
//
// This could be viewed as a struct containing a single bit and an n-1-bit
// value, but if we wrote it like that it would be more than a single word,
// and even a newtype around usize would be clumsy because we need atomics.
// But we use such a tuple for the return type of increase().
//
// Stealing a bit is fine because it just amounts to assuming that each
// panicking thread consumes at least 2 bytes of address space.
static GLOBAL_PANIC_COUNT: Atomic<usize> = AtomicUsize::new(0);
// Increases the global and local panic count, and returns whether an
// immediate abort is required.
//
// This also updates thread-local state to keep track of whether a panic
// hook is currently executing.
pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
if global_count & ALWAYS_ABORT_FLAG != 0 {
// Do *not* access thread-local state, we might be after a `fork`.
return Some(MustAbort::AlwaysAbort);
}
LOCAL_PANIC_COUNT.with(|c| {
let (count, in_panic_hook) = c.get();
if in_panic_hook {
return Some(MustAbort::PanicInHook);
}
c.set((count + 1, run_panic_hook));
None
})
}
pub fn finished_panic_hook() {
LOCAL_PANIC_COUNT.with(|c| {
let (count, _) = c.get();
c.set((count, false));
});
}
pub fn decrease() {
GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
LOCAL_PANIC_COUNT.with(|c| {
let (count, _) = c.get();
c.set((count - 1, false));
});
}
pub fn set_always_abort() {
GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed);
}
// Disregards ALWAYS_ABORT_FLAG
#[must_use]
pub fn get_count() -> usize {
LOCAL_PANIC_COUNT.with(|c| c.get().0)
}
// Disregards ALWAYS_ABORT_FLAG
#[must_use]
#[inline]
pub fn count_is_zero() -> bool {
if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 {
// Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
// (including the current one) will have `LOCAL_PANIC_COUNT`
// equal to zero, so TLS access can be avoided.
//
// In terms of performance, a relaxed atomic load is similar to a normal
// aligned memory read (e.g., a mov instruction in x86), but with some
// compiler optimization restrictions. On the other hand, a TLS access
// might require calling a non-inlinable function (such as `__tls_get_addr`
// when using the GD TLS model).
true
} else {
is_zero_slow_path()
}
}
// Slow path is in a separate function to reduce the amount of code
// inlined from `count_is_zero`.
#[inline(never)]
#[cold]
fn is_zero_slow_path() -> bool {
LOCAL_PANIC_COUNT.with(|c| c.get().0 == 0)
}
}
#[cfg(test)]
pub use realstd::rt::panic_count;
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
#[cfg(panic = "immediate-abort")]
pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
Ok(f())
}
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
#[cfg(not(panic = "immediate-abort"))]
pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
union Data<F, R> {
f: ManuallyDrop<F>,
r: ManuallyDrop<R>,
p: ManuallyDrop<Box<dyn Any + Send>>,
}
// We do some sketchy operations with ownership here for the sake of
// performance. We can only pass pointers down to `do_call` (can't pass
// objects by value), so we do all the ownership tracking here manually
// using a union.
//
// We go through a transition where:
//
// * First, we set the data field `f` to be the argumentless closure that we're going to call.
// * When we make the function call, the `do_call` function below, we take
// ownership of the function pointer. At this point the `data` union is
// entirely uninitialized.
// * If the closure successfully returns, we write the return value into the
// data's return slot (field `r`).
// * If the closure panics (`do_catch` below), we write the panic payload into field `p`.
// * Finally, when we come back out of the `try` intrinsic we're
// in one of two states:
//
// 1. The closure didn't panic, in which case the return value was
// filled in. We move it out of `data.r` and return it.
// 2. The closure panicked, in which case the panic payload was
// filled in. We move it out of `data.p` and return it.
//
// Once we stack all that together we should have the "most efficient'
// method of calling a catch panic whilst juggling ownership.
let mut data = Data { f: ManuallyDrop::new(f) };
let data_ptr = (&raw mut data) as *mut u8;
// SAFETY:
//
// Access to the union's fields: this is `std` and we know that the `catch_unwind`
// intrinsic fills in the `r` or `p` union field based on its return value.
//
// The call to `intrinsics::catch_unwind` is made safe by:
// - `do_call`, the first argument, can be called with the initial `data_ptr`.
// - `do_catch`, the second argument, can be called with the `data_ptr` as well.
// See their safety preconditions for more information
unsafe {
return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
Ok(ManuallyDrop::into_inner(data.r))
} else {
Err(ManuallyDrop::into_inner(data.p))
};
}
// We consider unwinding to be rare, so mark this function as cold. However,
// do not mark it no-inline -- that decision is best to leave to the
// optimizer (in most cases this function is not inlined even as a normal,
// non-cold function, though, as of the writing of this comment).
#[cold]
#[optimize(size)]
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
// SAFETY: The whole unsafe block hinges on a correct implementation of
// the panic handler `__rust_panic_cleanup`. As such we can only
// assume it returns the correct thing for `Box::from_raw` to work
// without undefined behavior.
let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
panic_count::decrease();
obj
}
// SAFETY:
// data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
// Its must contains a valid `f` (type: F) value that can be use to fill
// `data.r`.
//
// This function cannot be marked as `unsafe` because `intrinsics::catch_unwind`
// expects normal function pointers.
#[inline]
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
// SAFETY: this is the responsibility of the caller, see above.
unsafe {
let data = data as *mut Data<F, R>;
let data = &mut (*data);
let f = ManuallyDrop::take(&mut data.f);
data.r = ManuallyDrop::new(f());
}
}
// We *do* want this part of the catch to be inlined: this allows the
// compiler to properly track accesses to the Data union and optimize it
// away most of the time.
//
// SAFETY:
// data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
// Since this uses `cleanup` it also hinges on a correct implementation of
// `__rustc_panic_cleanup`.
//
// This function cannot be marked as `unsafe` because `intrinsics::catch_unwind`
// expects normal function pointers.
#[inline]
#[rustc_nounwind] // `intrinsic::catch_unwind` requires catch fn to be nounwind
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
// SAFETY: this is the responsibility of the caller, see above.
//
// When `__rustc_panic_cleaner` is correctly implemented we can rely
// on `obj` being the correct thing to pass to `data.p` (after wrapping
// in `ManuallyDrop`).
unsafe {
let data = data as *mut Data<F, R>;
let data = &mut (*data);
let obj = cleanup(payload);
data.p = ManuallyDrop::new(obj);
}
}
}
/// Determines whether the current thread is unwinding because of panic.
#[inline]
pub fn panicking() -> bool {
!panic_count::count_is_zero()
}
/// Entry point of panics from the core crate (`panic_impl` lang item).
#[cfg(not(any(test, doctest)))]
#[panic_handler]
pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
struct FormatStringPayload<'a> {
inner: &'a core::panic::PanicMessage<'a>,
string: Option<String>,
}
impl FormatStringPayload<'_> {
fn fill(&mut self) -> &mut String {
let inner = self.inner;
// Lazily, the first time this gets called, run the actual string formatting.
self.string.get_or_insert_with(|| {
let mut s = String::new();
let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new());
let _err = fmt::Display::fmt(&inner, &mut fmt);
s
})
}
}
unsafe impl PanicPayload for FormatStringPayload<'_> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
// We do two allocations here, unfortunately. But (a) they're required with the current
// scheme, and (b) we don't handle panic + OOM properly anyway (see comment in
// begin_panic below).
let contents = mem::take(self.fill());
Box::into_raw(Box::new(contents))
}
fn get(&mut self) -> &(dyn Any + Send) {
self.fill()
}
}
impl fmt::Display for FormatStringPayload<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(s) = &self.string {
f.write_str(s)
} else {
fmt::Display::fmt(&self.inner, f)
}
}
}
struct StaticStrPayload(&'static str);
unsafe impl PanicPayload for StaticStrPayload {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(Box::new(self.0))
}
fn get(&mut self) -> &(dyn Any + Send) {
&self.0
}
fn as_str(&mut self) -> Option<&str> {
Some(self.0)
}
}
impl fmt::Display for StaticStrPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
let loc = info.location().unwrap(); // The current implementation always returns Some
let msg = info.message();
crate::sys::backtrace::__rust_end_short_backtrace(move || {
if let Some(s) = msg.as_str() {
panic_with_hook(
&mut StaticStrPayload(s),
loc,
info.can_unwind(),
info.force_no_backtrace(),
);
} else {
panic_with_hook(
&mut FormatStringPayload { inner: &msg, string: None },
loc,
info.can_unwind(),
info.force_no_backtrace(),
);
}
})
}
/// This is the entry point of panicking for the non-format-string variants of
/// panic!() and assert!(). In particular, this is the only entry point that supports
/// arbitrary payloads, not just format strings.
#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
#[cfg_attr(not(any(test, doctest)), lang = "begin_panic")]
// lang item for CTFE panic support
// never inline unless panic=immediate-abort to avoid code
// bloat at the call sites as much as possible
#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))]
#[cfg_attr(panic = "immediate-abort", inline)]
#[track_caller]
#[rustc_do_not_const_check] // hooked by const-eval
pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
if cfg!(panic = "immediate-abort") {
intrinsics::abort()
}
struct Payload<A> {
inner: Option<A>,
}
unsafe impl<A: Send + 'static> PanicPayload for Payload<A> {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
// Note that this should be the only allocation performed in this code path. Currently
// this means that panic!() on OOM will invoke this code path, but then again we're not
// really ready for panic on OOM anyway. If we do start doing this, then we should
// propagate this allocation to be performed in the parent of this thread instead of the
// thread that's panicking.
let data = match self.inner.take() {
Some(a) => Box::new(a) as Box<dyn Any + Send>,
None => process::abort(),
};
Box::into_raw(data)
}
fn get(&mut self) -> &(dyn Any + Send) {
match self.inner {
Some(ref a) => a,
None => process::abort(),
}
}
}
impl<A: 'static> fmt::Display for Payload<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
Some(a) => f.write_str(payload_as_str(a)),
None => process::abort(),
}
}
}
let loc = Location::caller();
crate::sys::backtrace::__rust_end_short_backtrace(move || {
panic_with_hook(
&mut Payload { inner: Some(msg) },
loc,
/* can_unwind */ true,
/* force_no_backtrace */ false,
)
})
}
fn payload_as_str(payload: &dyn Any) -> &str {
if let Some(&s) = payload.downcast_ref::<&'static str>() {
s
} else if let Some(s) = payload.downcast_ref::<String>() {
s.as_str()
} else {
"Box<dyn Any>"
}
}
/// Central point for dispatching panics.
///
/// Executes the primary logic for a panic, including checking for recursive
/// panics, panic hooks, and finally dispatching to the panic runtime to either
/// abort or unwind.
#[optimize(size)]
fn panic_with_hook(
payload: &mut dyn PanicPayload,
location: &Location<'_>,
can_unwind: bool,
force_no_backtrace: bool,
) -> ! {
let must_abort = panic_count::increase(true);
// Check if we need to abort immediately.
if let Some(must_abort) = must_abort {
match must_abort {
panic_count::MustAbort::PanicInHook => {
// Don't try to format the message in this case, perhaps that is causing the
// recursive panics. However if the message is just a string, no user-defined
// code is involved in printing it, so that is risk-free.
let message: &str = payload.as_str().unwrap_or_default();
rtprintpanic!(
"panicked at {location}:\n{message}\nthread panicked while processing panic. aborting.\n"
);
}
panic_count::MustAbort::AlwaysAbort => {
// Unfortunately, this does not print a backtrace, because creating
// a `Backtrace` will allocate, which we must avoid here.
rtprintpanic!("aborting due to panic at {location}:\n{payload}\n");
}
}
crate::process::abort();
}
match *HOOK.read() {
// Some platforms (like wasm) know that printing to stderr won't ever actually
// print anything, and if that's the case we can skip the default
// hook. Since string formatting happens lazily when calling `payload`
// methods, this means we avoid formatting the string at all!
// (The panic runtime might still call `payload.take_box()` though and trigger
// formatting.)
Hook::Default if panic_output().is_none() => {}
Hook::Default => {
default_hook(&PanicHookInfo::new(
location,
payload.get(),
can_unwind,
force_no_backtrace,
));
}
Hook::Custom(ref hook) => {
hook(&PanicHookInfo::new(location, payload.get(), can_unwind, force_no_backtrace));
}
}
// Indicate that we have finished executing the panic hook. After this point
// it is fine if there is a panic while executing destructors, as long as it
// it contained within a `catch_unwind`.
panic_count::finished_panic_hook();
if !can_unwind {
// If a thread panics while running destructors or tries to unwind
// through a nounwind function (e.g. extern "C") then we cannot continue
// unwinding and have to abort immediately.
rtprintpanic!("thread caused non-unwinding panic. aborting.\n");
crate::process::abort();
}
rust_panic(payload)
}
/// This is the entry point for `resume_unwind`.
/// It just forwards the payload to the panic runtime.
#[cfg_attr(panic = "immediate-abort", inline)]
pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! {
panic_count::increase(false);
struct RewrapBox(Box<dyn Any + Send>);
unsafe impl PanicPayload for RewrapBox {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(mem::replace(&mut self.0, Box::new(())))
}
fn get(&mut self) -> &(dyn Any + Send) {
&*self.0
}
}
impl fmt::Display for RewrapBox {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(payload_as_str(&self.0))
}
}
rust_panic(&mut RewrapBox(payload))
}
/// A function with a fixed suffix (through `rustc_std_internal_symbol`)
/// on which to slap yer breakpoints.
#[inline(never)]
#[cfg_attr(not(test), rustc_std_internal_symbol)]
#[cfg(not(panic = "immediate-abort"))]
fn rust_panic(msg: &mut dyn PanicPayload) -> ! {
let code = unsafe { __rust_start_panic(msg) };
rtabort!("failed to initiate panic, error {code}")
}
#[cfg_attr(not(test), rustc_std_internal_symbol)]
#[cfg(panic = "immediate-abort")]
fn rust_panic(_: &mut dyn PanicPayload) -> ! {
crate::intrinsics::abort();
}

View File

@@ -150,11 +150,11 @@ pub mod rust_2024 {
}
}
#[panic_handler]
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
// TODO print
loop {}
}
// #[panic_handler]
// fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
// // TODO print
// loop {}
// }
/// # Safety
/// `argc` and `argv` are passed by the kernel

View File

@@ -27,3 +27,7 @@ impl Termination for isize {
ExitCode(self)
}
}
pub fn abort() -> ! {
loop {}
}

View File

@@ -1,5 +1,7 @@
macro_rules! rtabort {
($($t:tt)*) => {{}};
($($t:tt)*) => {{
loop {}
}};
}
macro_rules! rtprintpanic {
($($t:tt)*) => {{}};

View File

@@ -16,10 +16,70 @@ pub use once::OnceState;
pub use poison::LockResult;
pub use poison::Mutex;
pub use poison::MutexGuard;
pub use poison::PoisonError;
pub use poison::TryLockError;
pub use poison::TryLockResult;
pub use poison::Condvar;
pub use poison::RwLock;
pub use once_lock::OnceLock;
pub use reentrant_lock::ReentrantLock;
pub use reentrant_lock::ReentrantLockGuard;
pub use alloc_crate::sync::Arc;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[stable(feature = "wait_timeout", since = "1.5.0")]
pub struct WaitTimeoutResult(bool);
impl WaitTimeoutResult {
/// Returns `true` if the wait was known to have timed out.
///
/// # Examples
///
/// This example spawns a thread which will sleep 20 milliseconds before
/// updating a boolean value and then notifying the condvar.
///
/// The main thread will wait with a 10 millisecond timeout on the condvar
/// and will leave the loop upon timeout.
///
/// ```
/// use std::sync::{Arc, Condvar, Mutex};
/// use std::thread;
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
/// let pair2 = Arc::clone(&pair);
///
/// # let handle =
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
///
/// // Let's wait 20 milliseconds before notifying the condvar.
/// thread::sleep(Duration::from_millis(20));
///
/// let mut started = lock.lock().unwrap();
/// // We update the boolean value.
/// *started = true;
/// cvar.notify_one();
/// });
///
/// // Wait for the thread to start up.
/// let (lock, cvar) = &*pair;
/// loop {
/// // Let's put a timeout on the condvar's wait.
/// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
/// // 10 milliseconds have passed.
/// if result.1.timed_out() {
/// // timed out now and we can leave.
/// break
/// }
/// }
/// # // Prevent leaks for Miri.
/// # let _ = handle.join();
/// ```
#[must_use]
#[stable(feature = "wait_timeout", since = "1.5.0")]
pub fn timed_out(&self) -> bool {
self.0
}
}

View File

@@ -1,5 +1,5 @@
use crate::fmt;
use core::panic::RefUnwindSafe;
use crate::panic::RefUnwindSafe;
use crate::sync::nonpoison::{Condvar, Mutex};
/// A barrier enables multiple threads to synchronize the beginning

View File

@@ -2,7 +2,7 @@ use super::once::OnceExclusiveState;
use crate::cell::UnsafeCell;
use crate::mem::ManuallyDrop;
use crate::ops::{Deref, DerefMut};
use core::panic::{RefUnwindSafe, UnwindSafe};
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::sync::Once;
use crate::{fmt, ptr};

View File

@@ -150,7 +150,7 @@ mod zero;
pub use error::*;
use crate::fmt;
use core::panic::{RefUnwindSafe, UnwindSafe};
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::time::{Duration, Instant};
/// Creates a new asynchronous channel, returning the sender/receiver halves.

View File

@@ -4,7 +4,7 @@
//! example use case would be for initializing an FFI library.
use crate::fmt;
use core::panic::{RefUnwindSafe, UnwindSafe};
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::sys::sync as sys;
/// A low-level synchronization primitive for one-time global execution.

View File

@@ -3,7 +3,7 @@ use crate::cell::UnsafeCell;
use crate::fmt;
use crate::marker::PhantomData;
use crate::mem::MaybeUninit;
use core::panic::{RefUnwindSafe, UnwindSafe};
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::sync::Once;
/// A synchronization primitive which can nominally be written to only once.

View File

@@ -88,8 +88,8 @@ use crate::sys::sync as sys;
/// [`unwrap()`]: Result::unwrap
/// [`PoisonError`]: super::PoisonError
/// [`into_inner`]: super::PoisonError::into_inner
/// [panic hook]: core::panic::set_hook
/// [`catch_unwind`]: core::panic::catch_unwind
/// [panic hook]: crate::panic::set_hook
/// [`catch_unwind`]: crate::panic::catch_unwind
/// [`Cell`]: crate::cell::Cell
///
/// # Examples

View File

@@ -1,7 +1,7 @@
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::ops::Deref;
use core::panic::{RefUnwindSafe, UnwindSafe};
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::sys::sync as sys;
use crate::thread::{ThreadId, current_id};

View File

@@ -0,0 +1,242 @@
//! Common code for printing backtraces.
#![forbid(unsafe_op_in_unsafe_fn)]
// use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
use crate::borrow::Cow;
use crate::io::prelude::*;
use crate::path::{self, Path, PathBuf};
use crate::sync::{Mutex, MutexGuard, PoisonError};
use crate::{env, fmt, io};
/// Max number of frames to print.
const MAX_NB_FRAMES: usize = 100;
pub(crate) const FULL_BACKTRACE_DEFAULT: bool = cfg_select! {
// Fuchsia components default to full backtrace.
target_os = "fuchsia" => true,
_ => false,
};
pub(crate) struct BacktraceLock<'a>(#[allow(dead_code)] MutexGuard<'a, ()>);
pub(crate) fn lock<'a>() -> BacktraceLock<'a> {
static LOCK: Mutex<()> = Mutex::new(());
BacktraceLock(LOCK.lock().unwrap_or_else(PoisonError::into_inner))
}
// impl BacktraceLock<'_> {
// /// Prints the current backtrace.
// pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
// // There are issues currently linking libbacktrace into tests, and in
// // general during std's own unit tests we're not testing this path. In
// // test mode immediately return here to optimize away any references to the
// // libbacktrace symbols
// if cfg!(test) {
// return Ok(());
// }
// struct DisplayBacktrace {
// format: PrintFmt,
// }
// impl fmt::Display for DisplayBacktrace {
// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
// // SAFETY: the backtrace lock is held
// unsafe { _print_fmt(fmt, self.format) }
// }
// }
// write!(w, "{}", DisplayBacktrace { format })
// }
// }
// /// # Safety
// ///
// /// This function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program.
// unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
// // Always 'fail' to get the cwd when running under Miri -
// // this allows Miri to display backtraces in isolation mode
// let cwd = if !cfg!(miri) {
// env::current_dir().ok()
// } else {
// None
// };
// let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
// output_filename(fmt, bows, print_fmt, cwd.as_ref())
// };
// writeln!(fmt, "stack backtrace:")?;
// let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
// bt_fmt.add_context()?;
// let mut idx = 0;
// let mut res = Ok(());
// let mut omitted_count: usize = 0;
// let mut first_omit = true;
// // If we're using a short backtrace, ignore all frames until we're told to start printing.
// let mut print = print_fmt != PrintFmt::Short;
// set_image_base();
// // SAFETY: we roll our own locking in this town
// unsafe {
// backtrace_rs::trace_unsynchronized(|frame| {
// if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
// return false;
// }
// if cfg!(feature = "backtrace-trace-only") {
// const HEX_WIDTH: usize = 2 + 2 * size_of::<usize>();
// let frame_ip = frame.ip();
// res = writeln!(bt_fmt.formatter(), "{idx:4}: {frame_ip:HEX_WIDTH$?}");
// } else {
// let mut hit = false;
// backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
// hit = true;
// // `__rust_end_short_backtrace` means we are done hiding symbols
// // for now. Print until we see `__rust_begin_short_backtrace`.
// if print_fmt == PrintFmt::Short {
// if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
// if sym.contains("__rust_end_short_backtrace") {
// print = true;
// return;
// }
// if print && sym.contains("__rust_begin_short_backtrace") {
// print = false;
// return;
// }
// if !print {
// omitted_count += 1;
// }
// }
// }
// if print {
// if omitted_count > 0 {
// debug_assert!(print_fmt == PrintFmt::Short);
// // only print the message between the middle of frames
// if !first_omit {
// let _ = writeln!(
// bt_fmt.formatter(),
// " [... omitted {} frame{} ...]",
// omitted_count,
// if omitted_count > 1 { "s" } else { "" }
// );
// }
// first_omit = false;
// omitted_count = 0;
// }
// res = bt_fmt.frame().symbol(frame, symbol);
// }
// });
// #[cfg(all(target_os = "nto", any(target_env = "nto70", target_env = "nto71")))]
// if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
// if !hit && print {
// use crate::backtrace_rs::SymbolName;
// res = bt_fmt.frame().print_raw(
// frame.ip(),
// Some(SymbolName::new("__my_thread_exit".as_bytes())),
// None,
// None,
// );
// }
// return false;
// }
// if !hit && print {
// res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
// }
// }
// idx += 1;
// res.is_ok()
// })
// };
// res?;
// bt_fmt.finish()?;
// if print_fmt == PrintFmt::Short {
// writeln!(
// fmt,
// "note: Some details are omitted, \
// run with `RUST_BACKTRACE=full` for a verbose backtrace."
// )?;
// }
// Ok(())
// }
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
/// this is only inline(never) when backtraces in std are enabled, otherwise
/// it's fine to optimize away.
#[cfg_attr(feature = "backtrace", inline(never))]
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
where
F: FnOnce() -> T,
{
let result = f();
// prevent this frame from being tail-call optimised away
crate::hint::black_box(());
result
}
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
/// this is only inline(never) when backtraces in std are enabled, otherwise
/// it's fine to optimize away.
#[cfg_attr(feature = "backtrace", inline(never))]
pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
where
F: FnOnce() -> T,
{
let result = f();
// prevent this frame from being tail-call optimised away
crate::hint::black_box(());
result
}
// /// Prints the filename of the backtrace frame.
// ///
// /// See also `output`.
// pub fn output_filename(
// fmt: &mut fmt::Formatter<'_>,
// bows: BytesOrWideString<'_>,
// print_fmt: PrintFmt,
// cwd: Option<&PathBuf>,
// ) -> fmt::Result {
// let file: Cow<'_, Path> = match bows {
// #[cfg(unix)]
// BytesOrWideString::Bytes(bytes) => {
// use crate::os::unix::prelude::*;
// Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
// }
// #[cfg(not(unix))]
// BytesOrWideString::Bytes(bytes) => {
// Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
// }
// #[cfg(windows)]
// BytesOrWideString::Wide(wide) => {
// use crate::os::windows::prelude::*;
// Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
// }
// #[cfg(not(windows))]
// BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
// };
// if print_fmt == PrintFmt::Short && file.is_absolute() {
// if let Some(cwd) = cwd {
// if let Ok(stripped) = file.strip_prefix(&cwd) {
// if let Some(s) = stripped.to_str() {
// return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR);
// }
// }
// }
// }
// fmt::Display::fmt(&file.display(), fmt)
// }
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
pub fn set_image_base() {
let image_base = crate::os::fortanix_sgx::mem::image_base();
backtrace_rs::set_image_base(crate::ptr::without_provenance_mut(image_base as _));
}
#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))]
pub fn set_image_base() {
// nothing to do for platforms other than SGX
}

View File

@@ -14,6 +14,7 @@ pub mod alloc;
pub mod io;
pub mod pipe;
pub mod stdio;
pub mod backtrace;
// pub mod fs;
/// A trait for viewing representations from std types.

View File

@@ -2,8 +2,10 @@ mod condvar;
mod mutex;
mod once;
mod rwlock;
mod thread_parking;
pub use condvar::Condvar;
pub use mutex::Mutex;
pub use once::{Once, OnceState};
pub use rwlock::RwLock;
pub use thread_parking::Parker;

View File

@@ -0,0 +1,49 @@
cfg_select! {
any(
all(target_os = "windows", not(target_vendor = "win7")),
target_os = "linux",
target_os = "android",
all(target_arch = "wasm32", target_feature = "atomics"),
target_os = "freebsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "motor",
target_os = "hermit",
) => {
mod futex;
pub use futex::Parker;
}
any(
target_os = "netbsd",
all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "solid_asp3",
) => {
mod id;
pub use id::Parker;
}
target_vendor = "win7" => {
mod windows7;
pub use windows7::Parker;
}
all(target_vendor = "apple", not(miri)) => {
// Doesn't work in Miri, see <https://github.com/rust-lang/miri/issues/2589>.
mod darwin;
pub use darwin::Parker;
}
target_os = "xous" => {
mod xous;
pub use xous::Parker;
}
any(
target_family = "unix",
target_os = "teeos",
) => {
mod pthread;
pub use pthread::Parker;
}
_ => {
mod unsupported;
pub use unsupported::Parker;
}
}

View File

@@ -0,0 +1,11 @@
use crate::pin::Pin;
use crate::time::Duration;
pub struct Parker {}
impl Parker {
pub unsafe fn new_in_place(_parker: *mut Parker) {}
pub unsafe fn park(self: Pin<&Self>) {}
pub unsafe fn park_timeout(self: Pin<&Self>, _dur: Duration) {}
pub fn unpark(self: Pin<&Self>) {}
}

View File

@@ -5,7 +5,7 @@ use crate::cell::Cell;
use crate::marker::PhantomData;
use crate::mem::ManuallyDrop;
use crate::ops::Deref;
use core::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use crate::ptr::{self, NonNull};
#[doc(hidden)]

View File

@@ -6,18 +6,23 @@ pub mod join_handle;
pub mod lifecycle;
pub mod local;
pub mod main_thread;
pub mod scoped;
pub mod spawnhook;
pub mod thread;
use core::any::Any;
pub use crate::sys::thread::yield_now;
pub(crate) use current::current_or_unnamed;
pub use current::current_id;
pub(crate) use current::current_or_unnamed;
pub(crate) use current::current_os_id;
pub(crate) use current::with_current_name;
pub use functions::sleep;
pub use id::ThreadId;
pub(crate) use lifecycle::ThreadInit;
pub use local::LocalKey;
pub use thread::Thread;
pub(crate) use lifecycle::ThreadInit;
pub use local::AccessError;
// Implementation details used by the thread_local!{} macro.
#[doc(hidden)]
@@ -30,3 +35,7 @@ pub mod local_impl {
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(search_unbox)]
pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
pub fn panicking() -> ! {
todo!()
}

View File

@@ -7,7 +7,7 @@ use crate::mem::forget;
use crate::num::NonZero;
use crate::sys::thread as imp;
use crate::time::{Duration, Instant};
use crate::io;use core::panicking;
use crate::{io, panicking};
/// Spawns a new thread, returning a [`JoinHandle`] for it.
///

View File

@@ -11,7 +11,7 @@ use crate::mem::{ManuallyDrop, MaybeUninit};
use alloc_crate::sync::Arc;
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
use crate::sys::{AsInner, IntoInner, thread as imp};
use crate::{env, io};use core::panic;
use crate::{env, io, panic};
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(super) unsafe fn spawn_unchecked<'scope, F, T>(

View File

@@ -0,0 +1,358 @@
use super::Result;
use super::builder::Builder;
use super::current::current_or_unnamed;
use super::lifecycle::{JoinInner, spawn_unchecked};
use super::thread::Thread;
use crate::marker::PhantomData;
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use alloc_crate::sync::Arc;
use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering};
use crate::{fmt, io};
/// A scope to spawn scoped threads in.
///
/// See [`scope`] for details.
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub struct Scope<'scope, 'env: 'scope> {
data: Arc<ScopeData>,
/// Invariance over 'scope, to make sure 'scope cannot shrink,
/// which is necessary for soundness.
///
/// Without invariance, this would compile fine but be unsound:
///
/// ```compile_fail,E0373
/// std::thread::scope(|s| {
/// s.spawn(|| {
/// let a = String::from("abcd");
/// s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
/// });
/// });
/// ```
scope: PhantomData<&'scope mut &'scope ()>,
env: PhantomData<&'env mut &'env ()>,
}
/// An owned permission to join on a scoped thread (block on its termination).
///
/// See [`Scope::spawn`] for details.
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>);
pub(super) struct ScopeData {
num_running_threads: Atomic<usize>,
a_thread_panicked: Atomic<bool>,
main_thread: Thread,
}
impl ScopeData {
pub(super) fn increment_num_running_threads(&self) {
// We check for 'overflow' with usize::MAX / 2, to make sure there's no
// chance it overflows to 0, which would result in unsoundness.
if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 {
// This can only reasonably happen by mem::forget()'ing a lot of ScopedJoinHandles.
self.overflow();
}
}
#[cold]
fn overflow(&self) {
self.decrement_num_running_threads(false);
panic!("too many running threads in thread scope");
}
pub(super) fn decrement_num_running_threads(&self, panic: bool) {
if panic {
self.a_thread_panicked.store(true, Ordering::Relaxed);
}
if self.num_running_threads.fetch_sub(1, Ordering::Release) == 1 {
self.main_thread.unpark();
}
}
}
/// Creates a scope for spawning scoped threads.
///
/// The function passed to `scope` will be provided a [`Scope`] object,
/// through which scoped threads can be [spawned][`Scope::spawn`].
///
/// Unlike non-scoped threads, scoped threads can borrow non-`'static` data,
/// as the scope guarantees all threads will be joined at the end of the scope.
///
/// All threads spawned within the scope that haven't been manually joined
/// will be automatically joined before this function returns.
/// However, note that joining will only wait for the main function of these threads to finish; even
/// when this function returns, destructors of thread-local variables in these threads might still
/// be running.
///
/// # Panics
///
/// If any of the automatically joined threads panicked, this function will panic.
///
/// If you want to handle panics from spawned threads,
/// [`join`][ScopedJoinHandle::join] them before the end of the scope.
///
/// # Example
///
/// ```
/// use std::thread;
///
/// let mut a = vec![1, 2, 3];
/// let mut x = 0;
///
/// thread::scope(|s| {
/// s.spawn(|| {
/// println!("hello from the first scoped thread");
/// // We can borrow `a` here.
/// dbg!(&a);
/// });
/// s.spawn(|| {
/// println!("hello from the second scoped thread");
/// // We can even mutably borrow `x` here,
/// // because no other threads are using it.
/// x += a[0] + a[2];
/// });
/// println!("hello from the main thread");
/// });
///
/// // After the scope, we can modify and access our variables again:
/// a.push(4);
/// assert_eq!(x, a.len());
/// ```
///
/// # Lifetimes
///
/// Scoped threads involve two lifetimes: `'scope` and `'env`.
///
/// The `'scope` lifetime represents the lifetime of the scope itself.
/// That is: the time during which new scoped threads may be spawned,
/// and also the time during which they might still be running.
/// Once this lifetime ends, all scoped threads are joined.
/// This lifetime starts within the `scope` function, before `f` (the argument to `scope`) starts.
/// It ends after `f` returns and all scoped threads have been joined, but before `scope` returns.
///
/// The `'env` lifetime represents the lifetime of whatever is borrowed by the scoped threads.
/// This lifetime must outlast the call to `scope`, and thus cannot be smaller than `'scope`.
/// It can be as small as the call to `scope`, meaning that anything that outlives this call,
/// such as local variables defined right before the scope, can be borrowed by the scoped threads.
///
/// The `'env: 'scope` bound is part of the definition of the `Scope` type.
#[track_caller]
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub fn scope<'env, F, T>(f: F) -> T
where
F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
{
// We put the `ScopeData` into an `Arc` so that other threads can finish their
// `decrement_num_running_threads` even after this function returns.
let scope = Scope {
data: Arc::new(ScopeData {
num_running_threads: AtomicUsize::new(0),
main_thread: current_or_unnamed(),
a_thread_panicked: AtomicBool::new(false),
}),
env: PhantomData,
scope: PhantomData,
};
// Run `f`, but catch panics so we can make sure to wait for all the threads to join.
let result = catch_unwind(AssertUnwindSafe(|| f(&scope)));
// Wait until all the threads are finished.
while scope.data.num_running_threads.load(Ordering::Acquire) != 0 {
// SAFETY: this is the main thread, the handle belongs to us.
unsafe { scope.data.main_thread.park() };
}
// Throw any panic from `f`, or the return value of `f` if no thread panicked.
match result {
Err(e) => resume_unwind(e),
Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => {
panic!("a scoped thread panicked")
}
Ok(result) => result,
}
}
impl<'scope, 'env> Scope<'scope, 'env> {
/// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it.
///
/// Unlike non-scoped threads, threads spawned with this function may
/// borrow non-`'static` data from the outside the scope. See [`scope`] for
/// details.
///
/// 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 panic payload.
///
/// If the join handle is dropped, the spawned thread will be implicitly joined at the
/// end of the scope. In that case, if the spawned thread panics, [`scope`] will
/// panic after all threads are joined.
///
/// 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_scoped`].
///
/// # Panics
///
/// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`]
/// to recover from such errors.
///
/// [`join`]: ScopedJoinHandle::join
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
where
F: FnOnce() -> T + Send + 'scope,
T: Send + 'scope,
{
Builder::new().spawn_scoped(self, f).expect("failed to spawn thread")
}
}
impl Builder {
/// Spawns a new scoped thread using the settings set through this `Builder`.
///
/// Unlike [`Scope::spawn`], 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.
///
/// # Example
///
/// ```
/// use std::thread;
///
/// let mut a = vec![1, 2, 3];
/// let mut x = 0;
///
/// thread::scope(|s| {
/// thread::Builder::new()
/// .name("first".to_string())
/// .spawn_scoped(s, ||
/// {
/// println!("hello from the {:?} scoped thread", thread::current().name());
/// // We can borrow `a` here.
/// dbg!(&a);
/// })
/// .unwrap();
/// thread::Builder::new()
/// .name("second".to_string())
/// .spawn_scoped(s, ||
/// {
/// println!("hello from the {:?} scoped thread", thread::current().name());
/// // We can even mutably borrow `x` here,
/// // because no other threads are using it.
/// x += a[0] + a[2];
/// })
/// .unwrap();
/// println!("hello from the main thread");
/// });
///
/// // After the scope, we can modify and access our variables again:
/// a.push(4);
/// assert_eq!(x, a.len());
/// ```
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub fn spawn_scoped<'scope, 'env, F, T>(
self,
scope: &'scope Scope<'scope, 'env>,
f: F,
) -> io::Result<ScopedJoinHandle<'scope, T>>
where
F: FnOnce() -> T + Send + 'scope,
T: Send + 'scope,
{
let Builder { name, stack_size, no_hooks } = self;
Ok(ScopedJoinHandle(unsafe {
spawn_unchecked(name, stack_size, no_hooks, Some(scope.data.clone()), f)
}?))
}
}
impl<'scope, T> ScopedJoinHandle<'scope, T> {
/// Extracts a handle to the underlying thread.
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// thread::scope(|s| {
/// let t = s.spawn(|| {
/// println!("hello");
/// });
/// println!("thread id: {:?}", t.thread().id());
/// });
/// ```
#[must_use]
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub fn thread(&self) -> &Thread {
self.0.thread()
}
/// Waits for the associated thread to finish.
///
/// This function will return immediately if the associated thread has already finished.
/// Otherwise, it fully waits for the thread to finish, including all destructors
/// for thread-local variables that might be running after the main function of the thread.
///
/// In terms of [atomic memory orderings], the completion of the associated
/// thread synchronizes with this function returning.
/// In other words, all operations performed by that thread
/// [happen before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses)
/// all operations that happen after `join` returns.
///
/// If the associated thread panics, [`Err`] is returned with the panic payload.
///
/// [atomic memory orderings]: crate::sync::atomic
///
/// # Examples
///
/// ```
/// use std::thread;
///
/// thread::scope(|s| {
/// let t = s.spawn(|| {
/// panic!("oh no");
/// });
/// assert!(t.join().is_err());
/// });
/// ```
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub fn join(self) -> Result<T> {
self.0.join()
}
/// Checks if the associated thread has finished running its main function.
///
/// `is_finished` supports implementing a non-blocking join operation, by checking
/// `is_finished`, and calling `join` if it returns `true`. This function does not block. To
/// block while waiting on the thread to finish, use [`join`][Self::join].
///
/// This might return `true` for a brief moment after the thread's main
/// function has returned, but before the thread itself has stopped running.
/// However, once this returns `true`, [`join`][Self::join] can be expected
/// to return quickly, without blocking for any significant amount of time.
#[stable(feature = "scoped_threads", since = "1.63.0")]
pub fn is_finished(&self) -> bool {
self.0.is_finished()
}
}
#[stable(feature = "scoped_threads", since = "1.63.0")]
impl fmt::Debug for Scope<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Scope")
.field("num_running_threads", &self.data.num_running_threads.load(Ordering::Relaxed))
.field("a_thread_panicked", &self.data.a_thread_panicked.load(Ordering::Relaxed))
.field("main_thread", &self.data.main_thread)
.finish_non_exhaustive()
}
}
#[stable(feature = "scoped_threads", since = "1.63.0")]
impl<'scope, T> fmt::Debug for ScopedJoinHandle<'scope, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ScopedJoinHandle").finish_non_exhaustive()
}
}

View File

@@ -0,0 +1,153 @@
use super::thread::Thread;
use crate::cell::Cell;
use crate::iter;
use alloc_crate::sync::Arc;
crate::thread_local! {
/// A thread local linked list of spawn hooks.
///
/// It is a linked list of Arcs, such that it can very cheaply be inherited by spawned threads.
///
/// (That technically makes it a set of linked lists with shared tails, so a linked tree.)
static SPAWN_HOOKS: Cell<SpawnHooks> = const { Cell::new(SpawnHooks { first: None }) };
}
#[derive(Default, Clone)]
struct SpawnHooks {
first: Option<Arc<SpawnHook>>,
}
// Manually implement drop to prevent deep recursion when dropping linked Arc list.
impl Drop for SpawnHooks {
fn drop(&mut self) {
let mut next = self.first.take();
while let Some(SpawnHook { hook, next: n }) = next.and_then(|n| Arc::into_inner(n)) {
drop(hook);
next = n;
}
}
}
struct SpawnHook {
hook: Box<dyn Send + Sync + Fn(&Thread) -> Box<dyn Send + FnOnce()>>,
next: Option<Arc<SpawnHook>>,
}
/// Registers a function to run for every newly thread spawned.
///
/// The hook is executed in the parent thread, and returns a function
/// that will be executed in the new thread.
///
/// The hook is called with the `Thread` handle for the new thread.
///
/// The hook will only be added for the current thread and is inherited by the threads it spawns.
/// In other words, adding a hook has no effect on already running threads (other than the current
/// thread) and the threads they might spawn in the future.
///
/// Hooks can only be added, not removed.
///
/// The hooks will run in reverse order, starting with the most recently added.
///
/// # Usage
///
/// ```
/// #![feature(thread_spawn_hook)]
///
/// std::thread::add_spawn_hook(|_| {
/// ..; // This will run in the parent (spawning) thread.
/// move || {
/// ..; // This will run it the child (spawned) thread.
/// }
/// });
/// ```
///
/// # Example
///
/// A spawn hook can be used to "inherit" a thread local from the parent thread:
///
/// ```
/// #![feature(thread_spawn_hook)]
///
/// use std::cell::Cell;
///
/// thread_local! {
/// static X: Cell<u32> = Cell::new(0);
/// }
///
/// // This needs to be done once in the main thread before spawning any threads.
/// std::thread::add_spawn_hook(|_| {
/// // Get the value of X in the spawning thread.
/// let value = X.get();
/// // Set the value of X in the newly spawned thread.
/// move || X.set(value)
/// });
///
/// X.set(123);
///
/// std::thread::spawn(|| {
/// assert_eq!(X.get(), 123);
/// }).join().unwrap();
/// ```
#[unstable(feature = "thread_spawn_hook", issue = "132951")]
pub fn add_spawn_hook<F, G>(hook: F)
where
F: 'static + Send + Sync + Fn(&Thread) -> G,
G: 'static + Send + FnOnce(),
{
SPAWN_HOOKS.with(|h| {
let mut hooks = h.take();
let next = hooks.first.take();
hooks.first = Some(Arc::new(SpawnHook {
hook: Box::new(move |thread| Box::new(hook(thread))),
next,
}));
h.set(hooks);
});
}
/// Runs all the spawn hooks.
///
/// Called on the parent thread.
///
/// Returns the functions to be called on the newly spawned thread.
pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks {
// Get a snapshot of the spawn hooks.
// (Increments the refcount to the first node.)
if let Ok(hooks) = SPAWN_HOOKS.try_with(|hooks| {
let snapshot = hooks.take();
hooks.set(snapshot.clone());
snapshot
}) {
// Iterate over the hooks, run them, and collect the results in a vector.
let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref())
.map(|hook| (hook.hook)(thread))
.collect();
// Pass on the snapshot of the hooks and the results to the new thread,
// which will then run SpawnHookResults::run().
ChildSpawnHooks { hooks, to_run }
} else {
// TLS has been destroyed. Skip running the hooks.
// See https://github.com/rust-lang/rust/issues/138696
ChildSpawnHooks::default()
}
}
/// The results of running the spawn hooks.
///
/// This struct is sent to the new thread.
/// It contains the inherited hooks and the closures to be run.
#[derive(Default)]
pub(super) struct ChildSpawnHooks {
hooks: SpawnHooks,
to_run: Vec<Box<dyn FnOnce() + Send>>,
}
impl ChildSpawnHooks {
// This is run on the newly spawned thread, directly at the start.
pub(super) fn run(self) {
SPAWN_HOOKS.set(self.hooks);
for run in self.to_run {
run();
}
}
}

View File

@@ -28,6 +28,12 @@ update_std:
# @just cp_std "fs.rs"
@just cp_std "time.rs"
@just cp_std "bstr.rs"
@just cp_std "panicking.rs"
@just cp_std "panic.rs"
@just cp_std "collections/hash/map.rs"
@just cp_std "collections/hash/set.rs"
@just cp_std "collections/hash/mod.rs"
@just cp_std "collections/mod.rs"
@just cp_std "io/error.rs"
@just cp_std "io/error/repr_bitpacked.rs"
@just cp_std "io/error/repr_unpacked.rs"
@@ -44,7 +50,7 @@ update_std:
@just cp_std "io/copy/tests.rs"
@just cp_std "io/pipe.rs"
@just cp_std "io/pipe/tests.rs"
@just cp_std "io/stdio.rs"
# @just cp_std "io/stdio.rs"
@just cp_std "io/buffered/mod.rs"
@just cp_std "io/buffered/bufreader.rs"
@just cp_std "io/buffered/bufreader/buffer.rs"
@@ -92,6 +98,8 @@ update_std:
@just cp_std "thread/functions.rs"
@just cp_std "thread/lifecycle.rs"
@just cp_std "thread/builder.rs"
@just cp_std "thread/scoped.rs"
@just cp_std "thread/spawnhook.rs"
@just cp_std "sys/exit.rs"
@just cp_std "sys/env_consts.rs"
@just cp_std "sys/configure_builtins.rs"
@@ -104,6 +112,8 @@ update_std:
@just cp_std "sys/sync/once/no_threads.rs"
@just cp_std "sys/sync/rwlock/mod.rs"
@just cp_std "sys/sync/rwlock/no_threads.rs"
@just cp_std "sys/sync/thread_parking/mod.rs"
@just cp_std "sys/sync/thread_parking/unsupported.rs"
@just cp_std "sys/thread/mod.rs"
@just cp_std "sys/thread/unsupported.rs"
@just cp_std "sys/thread_local/no_threads.rs"

View File

@@ -5,9 +5,8 @@ s|alloc::slice::Join|alloc_crate::slice::Join|g
s|alloc::bstr|alloc_crate::bstr|g
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
s|crate::panic|core::panic|g
s|use crate::{io, panicking};|use crate::io;use core::panicking;|g
s|use crate::{env, io, panic};|use crate::{env, io};use core::panic;|g
# s|collections::HashMap|hashbrown::HashMap|g
/crate::backtrace_rs/c \ todo!()
# /target_os = "xous",/a \ target_os = "survos",
# Ajouter d'autres modifications facilement ici :