Add more from the std
This commit is contained in:
@@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
hashbrown = "0.16"
|
||||
os-std-macros = { path = "../os-std-macros" }
|
||||
shared = { path = "../shared", features = ["user"] }
|
||||
io = { path = "../io", features = ["alloc"] }
|
||||
|
||||
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 impls;
|
||||
pub mod pipe;
|
||||
// pub mod stdio;
|
||||
pub mod stdio;
|
||||
pub mod prelude;
|
||||
pub mod util;
|
||||
|
||||
@@ -18,7 +18,9 @@ use crate::ops::{Deref, DerefMut};
|
||||
use crate::{cmp, fmt, slice, str, sys};
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
pub use core::io::{BorrowedBuf, BorrowedCursor};
|
||||
pub use cursor::Cursor;
|
||||
use core::slice::memchr;
|
||||
pub use stdio::try_set_output_capture;
|
||||
|
||||
use crate::fs::File;
|
||||
use io::IoBase;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -267,6 +267,11 @@ pub use core::{
|
||||
stringify, trace_macros,
|
||||
};
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub mod ffi;
|
||||
pub mod hash;
|
||||
pub mod io;
|
||||
@@ -280,6 +285,10 @@ pub mod process;
|
||||
pub mod rt;
|
||||
pub mod alloc;
|
||||
pub mod bstr;
|
||||
pub mod collections;
|
||||
pub mod env;
|
||||
pub mod panic;
|
||||
pub mod panicking;
|
||||
pub mod sync;
|
||||
pub mod sys;
|
||||
pub mod thread;
|
||||
|
||||
534
crates/std/src/panic.rs
Normal file
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]
|
||||
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||
// TODO print
|
||||
loop {}
|
||||
}
|
||||
// #[panic_handler]
|
||||
// fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||
// // TODO print
|
||||
// loop {}
|
||||
// }
|
||||
|
||||
/// # Safety
|
||||
/// `argc` and `argv` are passed by the kernel
|
||||
|
||||
@@ -27,3 +27,7 @@ impl Termination for isize {
|
||||
ExitCode(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn abort() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
macro_rules! rtabort {
|
||||
($($t:tt)*) => {{}};
|
||||
($($t:tt)*) => {{
|
||||
loop {}
|
||||
}};
|
||||
}
|
||||
macro_rules! rtprintpanic {
|
||||
($($t:tt)*) => {{}};
|
||||
|
||||
@@ -16,10 +16,70 @@ pub use once::OnceState;
|
||||
|
||||
pub use poison::LockResult;
|
||||
pub use poison::Mutex;
|
||||
pub use poison::MutexGuard;
|
||||
pub use poison::PoisonError;
|
||||
pub use poison::TryLockError;
|
||||
pub use poison::TryLockResult;
|
||||
pub use poison::Condvar;
|
||||
pub use poison::RwLock;
|
||||
pub use once_lock::OnceLock;
|
||||
pub use reentrant_lock::ReentrantLock;
|
||||
pub use reentrant_lock::ReentrantLockGuard;
|
||||
pub use alloc_crate::sync::Arc;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub struct WaitTimeoutResult(bool);
|
||||
|
||||
impl WaitTimeoutResult {
|
||||
/// Returns `true` if the wait was known to have timed out.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This example spawns a thread which will sleep 20 milliseconds before
|
||||
/// updating a boolean value and then notifying the condvar.
|
||||
///
|
||||
/// The main thread will wait with a 10 millisecond timeout on the condvar
|
||||
/// and will leave the loop upon timeout.
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Condvar, Mutex};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// # let handle =
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
///
|
||||
/// // Let's wait 20 milliseconds before notifying the condvar.
|
||||
/// thread::sleep(Duration::from_millis(20));
|
||||
///
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // We update the boolean value.
|
||||
/// *started = true;
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// loop {
|
||||
/// // Let's put a timeout on the condvar's wait.
|
||||
/// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
|
||||
/// // 10 milliseconds have passed.
|
||||
/// if result.1.timed_out() {
|
||||
/// // timed out now and we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// # // Prevent leaks for Miri.
|
||||
/// # let _ = handle.join();
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub fn timed_out(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::fmt;
|
||||
use core::panic::RefUnwindSafe;
|
||||
use crate::panic::RefUnwindSafe;
|
||||
use crate::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
/// A barrier enables multiple threads to synchronize the beginning
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::once::OnceExclusiveState;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::Once;
|
||||
use crate::{fmt, ptr};
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ mod zero;
|
||||
pub use error::*;
|
||||
|
||||
use crate::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
/// Creates a new asynchronous channel, returning the sender/receiver halves.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! example use case would be for initializing an FFI library.
|
||||
|
||||
use crate::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A low-level synchronization primitive for one-time global execution.
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::MaybeUninit;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::Once;
|
||||
|
||||
/// A synchronization primitive which can nominally be written to only once.
|
||||
|
||||
@@ -88,8 +88,8 @@ use crate::sys::sync as sys;
|
||||
/// [`unwrap()`]: Result::unwrap
|
||||
/// [`PoisonError`]: super::PoisonError
|
||||
/// [`into_inner`]: super::PoisonError::into_inner
|
||||
/// [panic hook]: core::panic::set_hook
|
||||
/// [`catch_unwind`]: core::panic::catch_unwind
|
||||
/// [panic hook]: crate::panic::set_hook
|
||||
/// [`catch_unwind`]: crate::panic::catch_unwind
|
||||
/// [`Cell`]: crate::cell::Cell
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::ops::Deref;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sys::sync as sys;
|
||||
use crate::thread::{ThreadId, current_id};
|
||||
|
||||
|
||||
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 pipe;
|
||||
pub mod stdio;
|
||||
pub mod backtrace;
|
||||
// pub mod fs;
|
||||
|
||||
/// A trait for viewing representations from std types.
|
||||
|
||||
@@ -2,8 +2,10 @@ mod condvar;
|
||||
mod mutex;
|
||||
mod once;
|
||||
mod rwlock;
|
||||
mod thread_parking;
|
||||
|
||||
pub use condvar::Condvar;
|
||||
pub use mutex::Mutex;
|
||||
pub use once::{Once, OnceState};
|
||||
pub use rwlock::RwLock;
|
||||
pub use thread_parking::Parker;
|
||||
|
||||
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::mem::ManuallyDrop;
|
||||
use crate::ops::Deref;
|
||||
use core::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||
use crate::ptr::{self, NonNull};
|
||||
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -6,18 +6,23 @@ pub mod join_handle;
|
||||
pub mod lifecycle;
|
||||
pub mod local;
|
||||
pub mod main_thread;
|
||||
pub mod scoped;
|
||||
pub mod spawnhook;
|
||||
pub mod thread;
|
||||
|
||||
use core::any::Any;
|
||||
|
||||
pub use crate::sys::thread::yield_now;
|
||||
pub(crate) use current::current_or_unnamed;
|
||||
pub use current::current_id;
|
||||
pub(crate) use current::current_or_unnamed;
|
||||
pub(crate) use current::current_os_id;
|
||||
pub(crate) use current::with_current_name;
|
||||
pub use functions::sleep;
|
||||
pub use id::ThreadId;
|
||||
pub(crate) use lifecycle::ThreadInit;
|
||||
pub use local::LocalKey;
|
||||
pub use thread::Thread;
|
||||
pub(crate) use lifecycle::ThreadInit;
|
||||
pub use local::AccessError;
|
||||
|
||||
// Implementation details used by the thread_local!{} macro.
|
||||
#[doc(hidden)]
|
||||
@@ -30,3 +35,7 @@ pub mod local_impl {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(search_unbox)]
|
||||
pub type Result<T> = crate::result::Result<T, Box<dyn Any + Send + 'static>>;
|
||||
|
||||
pub fn panicking() -> ! {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::mem::forget;
|
||||
use crate::num::NonZero;
|
||||
use crate::sys::thread as imp;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::io;use core::panicking;
|
||||
use crate::{io, panicking};
|
||||
|
||||
/// Spawns a new thread, returning a [`JoinHandle`] for it.
|
||||
///
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::mem::{ManuallyDrop, MaybeUninit};
|
||||
use alloc_crate::sync::Arc;
|
||||
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
|
||||
use crate::sys::{AsInner, IntoInner, thread as imp};
|
||||
use crate::{env, io};use core::panic;
|
||||
use crate::{env, io, panic};
|
||||
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub(super) unsafe fn spawn_unchecked<'scope, F, T>(
|
||||
|
||||
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 "time.rs"
|
||||
@just cp_std "bstr.rs"
|
||||
@just cp_std "panicking.rs"
|
||||
@just cp_std "panic.rs"
|
||||
@just cp_std "collections/hash/map.rs"
|
||||
@just cp_std "collections/hash/set.rs"
|
||||
@just cp_std "collections/hash/mod.rs"
|
||||
@just cp_std "collections/mod.rs"
|
||||
@just cp_std "io/error.rs"
|
||||
@just cp_std "io/error/repr_bitpacked.rs"
|
||||
@just cp_std "io/error/repr_unpacked.rs"
|
||||
@@ -44,7 +50,7 @@ update_std:
|
||||
@just cp_std "io/copy/tests.rs"
|
||||
@just cp_std "io/pipe.rs"
|
||||
@just cp_std "io/pipe/tests.rs"
|
||||
@just cp_std "io/stdio.rs"
|
||||
# @just cp_std "io/stdio.rs"
|
||||
@just cp_std "io/buffered/mod.rs"
|
||||
@just cp_std "io/buffered/bufreader.rs"
|
||||
@just cp_std "io/buffered/bufreader/buffer.rs"
|
||||
@@ -92,6 +98,8 @@ update_std:
|
||||
@just cp_std "thread/functions.rs"
|
||||
@just cp_std "thread/lifecycle.rs"
|
||||
@just cp_std "thread/builder.rs"
|
||||
@just cp_std "thread/scoped.rs"
|
||||
@just cp_std "thread/spawnhook.rs"
|
||||
@just cp_std "sys/exit.rs"
|
||||
@just cp_std "sys/env_consts.rs"
|
||||
@just cp_std "sys/configure_builtins.rs"
|
||||
@@ -104,6 +112,8 @@ update_std:
|
||||
@just cp_std "sys/sync/once/no_threads.rs"
|
||||
@just cp_std "sys/sync/rwlock/mod.rs"
|
||||
@just cp_std "sys/sync/rwlock/no_threads.rs"
|
||||
@just cp_std "sys/sync/thread_parking/mod.rs"
|
||||
@just cp_std "sys/sync/thread_parking/unsupported.rs"
|
||||
@just cp_std "sys/thread/mod.rs"
|
||||
@just cp_std "sys/thread/unsupported.rs"
|
||||
@just cp_std "sys/thread_local/no_threads.rs"
|
||||
|
||||
@@ -5,9 +5,8 @@ s|alloc::slice::Join|alloc_crate::slice::Join|g
|
||||
s|alloc::bstr|alloc_crate::bstr|g
|
||||
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
||||
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
|
||||
s|crate::panic|core::panic|g
|
||||
s|use crate::{io, panicking};|use crate::io;use core::panicking;|g
|
||||
s|use crate::{env, io, panic};|use crate::{env, io};use core::panic;|g
|
||||
# s|collections::HashMap|hashbrown::HashMap|g
|
||||
/crate::backtrace_rs/c \ todo!()
|
||||
# /target_os = "xous",/a \ target_os = "survos",
|
||||
|
||||
# Ajouter d'autres modifications facilement ici :
|
||||
|
||||
Reference in New Issue
Block a user