Add more from the std
This commit is contained in:
@@ -4,6 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
hashbrown = "0.16"
|
||||||
os-std-macros = { path = "../os-std-macros" }
|
os-std-macros = { path = "../os-std-macros" }
|
||||||
shared = { path = "../shared", features = ["user"] }
|
shared = { path = "../shared", features = ["user"] }
|
||||||
io = { path = "../io", features = ["alloc"] }
|
io = { path = "../io", features = ["alloc"] }
|
||||||
|
|||||||
3034
crates/std/src/collections/hash/map.rs
Normal file
3034
crates/std/src/collections/hash/map.rs
Normal file
File diff suppressed because it is too large
Load Diff
4
crates/std/src/collections/hash/mod.rs
Normal file
4
crates/std/src/collections/hash/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
//! Unordered containers, implemented as hash-tables
|
||||||
|
|
||||||
|
pub mod map;
|
||||||
|
pub mod set;
|
||||||
2515
crates/std/src/collections/hash/set.rs
Normal file
2515
crates/std/src/collections/hash/set.rs
Normal file
File diff suppressed because it is too large
Load Diff
6
crates/std/src/collections/mod.rs
Normal file
6
crates/std/src/collections/mod.rs
Normal 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
5
crates/std/src/env.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use crate::ffi::OsString;
|
||||||
|
|
||||||
|
pub fn var_os(s: &str) -> Option<OsString> {
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ pub mod copy;
|
|||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
pub mod impls;
|
pub mod impls;
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
// pub mod stdio;
|
pub mod stdio;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
@@ -18,7 +18,9 @@ use crate::ops::{Deref, DerefMut};
|
|||||||
use crate::{cmp, fmt, slice, str, sys};
|
use crate::{cmp, fmt, slice, str, sys};
|
||||||
#[unstable(feature = "read_buf", issue = "78485")]
|
#[unstable(feature = "read_buf", issue = "78485")]
|
||||||
pub use core::io::{BorrowedBuf, BorrowedCursor};
|
pub use core::io::{BorrowedBuf, BorrowedCursor};
|
||||||
|
pub use cursor::Cursor;
|
||||||
use core::slice::memchr;
|
use core::slice::memchr;
|
||||||
|
pub use stdio::try_set_output_capture;
|
||||||
|
|
||||||
use crate::fs::File;
|
use crate::fs::File;
|
||||||
use io::IoBase;
|
use io::IoBase;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -267,6 +267,11 @@ pub use core::{
|
|||||||
stringify, trace_macros,
|
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 ffi;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
@@ -280,6 +285,10 @@ pub mod process;
|
|||||||
pub mod rt;
|
pub mod rt;
|
||||||
pub mod alloc;
|
pub mod alloc;
|
||||||
pub mod bstr;
|
pub mod bstr;
|
||||||
|
pub mod collections;
|
||||||
|
pub mod env;
|
||||||
|
pub mod panic;
|
||||||
|
pub mod panicking;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
|
|||||||
534
crates/std/src/panic.rs
Normal file
534
crates/std/src/panic.rs
Normal 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
894
crates/std/src/panicking.rs
Normal 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();
|
||||||
|
}
|
||||||
@@ -150,11 +150,11 @@ pub mod rust_2024 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
// #[panic_handler]
|
||||||
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
// fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||||
// TODO print
|
// // TODO print
|
||||||
loop {}
|
// loop {}
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `argc` and `argv` are passed by the kernel
|
/// `argc` and `argv` are passed by the kernel
|
||||||
|
|||||||
@@ -27,3 +27,7 @@ impl Termination for isize {
|
|||||||
ExitCode(self)
|
ExitCode(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn abort() -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
macro_rules! rtabort {
|
macro_rules! rtabort {
|
||||||
($($t:tt)*) => {{}};
|
($($t:tt)*) => {{
|
||||||
|
loop {}
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
macro_rules! rtprintpanic {
|
macro_rules! rtprintpanic {
|
||||||
($($t:tt)*) => {{}};
|
($($t:tt)*) => {{}};
|
||||||
|
|||||||
@@ -16,10 +16,70 @@ pub use once::OnceState;
|
|||||||
|
|
||||||
pub use poison::LockResult;
|
pub use poison::LockResult;
|
||||||
pub use poison::Mutex;
|
pub use poison::Mutex;
|
||||||
|
pub use poison::MutexGuard;
|
||||||
pub use poison::PoisonError;
|
pub use poison::PoisonError;
|
||||||
pub use poison::TryLockError;
|
pub use poison::TryLockError;
|
||||||
pub use poison::TryLockResult;
|
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)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||||
pub struct WaitTimeoutResult(bool);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use core::panic::RefUnwindSafe;
|
use crate::panic::RefUnwindSafe;
|
||||||
use crate::sync::nonpoison::{Condvar, Mutex};
|
use crate::sync::nonpoison::{Condvar, Mutex};
|
||||||
|
|
||||||
/// A barrier enables multiple threads to synchronize the beginning
|
/// A barrier enables multiple threads to synchronize the beginning
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::once::OnceExclusiveState;
|
|||||||
use crate::cell::UnsafeCell;
|
use crate::cell::UnsafeCell;
|
||||||
use crate::mem::ManuallyDrop;
|
use crate::mem::ManuallyDrop;
|
||||||
use crate::ops::{Deref, DerefMut};
|
use crate::ops::{Deref, DerefMut};
|
||||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
use crate::sync::Once;
|
use crate::sync::Once;
|
||||||
use crate::{fmt, ptr};
|
use crate::{fmt, ptr};
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ mod zero;
|
|||||||
pub use error::*;
|
pub use error::*;
|
||||||
|
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::{Duration, Instant};
|
||||||
|
|
||||||
/// Creates a new asynchronous channel, returning the sender/receiver halves.
|
/// Creates a new asynchronous channel, returning the sender/receiver halves.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//! example use case would be for initializing an FFI library.
|
//! example use case would be for initializing an FFI library.
|
||||||
|
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
use crate::sys::sync as sys;
|
use crate::sys::sync as sys;
|
||||||
|
|
||||||
/// A low-level synchronization primitive for one-time global execution.
|
/// A low-level synchronization primitive for one-time global execution.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::cell::UnsafeCell;
|
|||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::marker::PhantomData;
|
use crate::marker::PhantomData;
|
||||||
use crate::mem::MaybeUninit;
|
use crate::mem::MaybeUninit;
|
||||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
use crate::sync::Once;
|
use crate::sync::Once;
|
||||||
|
|
||||||
/// A synchronization primitive which can nominally be written to only once.
|
/// A synchronization primitive which can nominally be written to only once.
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ use crate::sys::sync as sys;
|
|||||||
/// [`unwrap()`]: Result::unwrap
|
/// [`unwrap()`]: Result::unwrap
|
||||||
/// [`PoisonError`]: super::PoisonError
|
/// [`PoisonError`]: super::PoisonError
|
||||||
/// [`into_inner`]: super::PoisonError::into_inner
|
/// [`into_inner`]: super::PoisonError::into_inner
|
||||||
/// [panic hook]: core::panic::set_hook
|
/// [panic hook]: crate::panic::set_hook
|
||||||
/// [`catch_unwind`]: core::panic::catch_unwind
|
/// [`catch_unwind`]: crate::panic::catch_unwind
|
||||||
/// [`Cell`]: crate::cell::Cell
|
/// [`Cell`]: crate::cell::Cell
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::cell::UnsafeCell;
|
use crate::cell::UnsafeCell;
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::ops::Deref;
|
use crate::ops::Deref;
|
||||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
use crate::sys::sync as sys;
|
use crate::sys::sync as sys;
|
||||||
use crate::thread::{ThreadId, current_id};
|
use crate::thread::{ThreadId, current_id};
|
||||||
|
|
||||||
|
|||||||
242
crates/std/src/sys/backtrace.rs
Normal file
242
crates/std/src/sys/backtrace.rs
Normal 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
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ pub mod alloc;
|
|||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
pub mod stdio;
|
pub mod stdio;
|
||||||
|
pub mod backtrace;
|
||||||
// pub mod fs;
|
// pub mod fs;
|
||||||
|
|
||||||
/// A trait for viewing representations from std types.
|
/// A trait for viewing representations from std types.
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ mod condvar;
|
|||||||
mod mutex;
|
mod mutex;
|
||||||
mod once;
|
mod once;
|
||||||
mod rwlock;
|
mod rwlock;
|
||||||
|
mod thread_parking;
|
||||||
|
|
||||||
pub use condvar::Condvar;
|
pub use condvar::Condvar;
|
||||||
pub use mutex::Mutex;
|
pub use mutex::Mutex;
|
||||||
pub use once::{Once, OnceState};
|
pub use once::{Once, OnceState};
|
||||||
pub use rwlock::RwLock;
|
pub use rwlock::RwLock;
|
||||||
|
pub use thread_parking::Parker;
|
||||||
|
|||||||
49
crates/std/src/sys/sync/thread_parking/mod.rs
Normal file
49
crates/std/src/sys/sync/thread_parking/mod.rs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
crates/std/src/sys/sync/thread_parking/unsupported.rs
Normal file
11
crates/std/src/sys/sync/thread_parking/unsupported.rs
Normal 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>) {}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ use crate::cell::Cell;
|
|||||||
use crate::marker::PhantomData;
|
use crate::marker::PhantomData;
|
||||||
use crate::mem::ManuallyDrop;
|
use crate::mem::ManuallyDrop;
|
||||||
use crate::ops::Deref;
|
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};
|
use crate::ptr::{self, NonNull};
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|||||||
@@ -6,18 +6,23 @@ pub mod join_handle;
|
|||||||
pub mod lifecycle;
|
pub mod lifecycle;
|
||||||
pub mod local;
|
pub mod local;
|
||||||
pub mod main_thread;
|
pub mod main_thread;
|
||||||
|
pub mod scoped;
|
||||||
|
pub mod spawnhook;
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
|
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
|
||||||
pub use crate::sys::thread::yield_now;
|
pub use crate::sys::thread::yield_now;
|
||||||
pub(crate) use current::current_or_unnamed;
|
|
||||||
pub use current::current_id;
|
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 functions::sleep;
|
||||||
pub use id::ThreadId;
|
pub use id::ThreadId;
|
||||||
|
pub(crate) use lifecycle::ThreadInit;
|
||||||
pub use local::LocalKey;
|
pub use local::LocalKey;
|
||||||
pub use thread::Thread;
|
pub use thread::Thread;
|
||||||
pub(crate) use lifecycle::ThreadInit;
|
pub use local::AccessError;
|
||||||
|
|
||||||
// Implementation details used by the thread_local!{} macro.
|
// Implementation details used by the thread_local!{} macro.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@@ -30,3 +35,7 @@ pub mod local_impl {
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[doc(search_unbox)]
|
#[doc(search_unbox)]
|
||||||
pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
|
pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
|
||||||
|
|
||||||
|
pub fn panicking() -> ! {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::mem::forget;
|
|||||||
use crate::num::NonZero;
|
use crate::num::NonZero;
|
||||||
use crate::sys::thread as imp;
|
use crate::sys::thread as imp;
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::{Duration, Instant};
|
||||||
use crate::io;use core::panicking;
|
use crate::{io, panicking};
|
||||||
|
|
||||||
/// Spawns a new thread, returning a [`JoinHandle`] for it.
|
/// Spawns a new thread, returning a [`JoinHandle`] for it.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::mem::{ManuallyDrop, MaybeUninit};
|
|||||||
use alloc_crate::sync::Arc;
|
use alloc_crate::sync::Arc;
|
||||||
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
|
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
|
||||||
use crate::sys::{AsInner, IntoInner, thread as imp};
|
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
|
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||||
pub(super) unsafe fn spawn_unchecked<'scope, F, T>(
|
pub(super) unsafe fn spawn_unchecked<'scope, F, T>(
|
||||||
|
|||||||
358
crates/std/src/thread/scoped.rs
Normal file
358
crates/std/src/thread/scoped.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
153
crates/std/src/thread/spawnhook.rs
Normal file
153
crates/std/src/thread/spawnhook.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
justfile
12
justfile
@@ -28,6 +28,12 @@ update_std:
|
|||||||
# @just cp_std "fs.rs"
|
# @just cp_std "fs.rs"
|
||||||
@just cp_std "time.rs"
|
@just cp_std "time.rs"
|
||||||
@just cp_std "bstr.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.rs"
|
||||||
@just cp_std "io/error/repr_bitpacked.rs"
|
@just cp_std "io/error/repr_bitpacked.rs"
|
||||||
@just cp_std "io/error/repr_unpacked.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/copy/tests.rs"
|
||||||
@just cp_std "io/pipe.rs"
|
@just cp_std "io/pipe.rs"
|
||||||
@just cp_std "io/pipe/tests.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/mod.rs"
|
||||||
@just cp_std "io/buffered/bufreader.rs"
|
@just cp_std "io/buffered/bufreader.rs"
|
||||||
@just cp_std "io/buffered/bufreader/buffer.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/functions.rs"
|
||||||
@just cp_std "thread/lifecycle.rs"
|
@just cp_std "thread/lifecycle.rs"
|
||||||
@just cp_std "thread/builder.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/exit.rs"
|
||||||
@just cp_std "sys/env_consts.rs"
|
@just cp_std "sys/env_consts.rs"
|
||||||
@just cp_std "sys/configure_builtins.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/once/no_threads.rs"
|
||||||
@just cp_std "sys/sync/rwlock/mod.rs"
|
@just cp_std "sys/sync/rwlock/mod.rs"
|
||||||
@just cp_std "sys/sync/rwlock/no_threads.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/mod.rs"
|
||||||
@just cp_std "sys/thread/unsupported.rs"
|
@just cp_std "sys/thread/unsupported.rs"
|
||||||
@just cp_std "sys/thread_local/no_threads.rs"
|
@just cp_std "sys/thread_local/no_threads.rs"
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ s|alloc::slice::Join|alloc_crate::slice::Join|g
|
|||||||
s|alloc::bstr|alloc_crate::bstr|g
|
s|alloc::bstr|alloc_crate::bstr|g
|
||||||
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
||||||
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
|
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
|
||||||
s|crate::panic|core::panic|g
|
# s|collections::HashMap|hashbrown::HashMap|g
|
||||||
s|use crate::{io, panicking};|use crate::io;use core::panicking;|g
|
/crate::backtrace_rs/c \ todo!()
|
||||||
s|use crate::{env, io, panic};|use crate::{env, io};use core::panic;|g
|
|
||||||
# /target_os = "xous",/a \ target_os = "survos",
|
# /target_os = "xous",/a \ target_os = "survos",
|
||||||
|
|
||||||
# Ajouter d'autres modifications facilement ici :
|
# Ajouter d'autres modifications facilement ici :
|
||||||
|
|||||||
Reference in New Issue
Block a user