Skip to content

thread name in stack overflow message #144500

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/hermit/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ impl Thread {
}
}

pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
unsafe {
Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/itron/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ impl Thread {
/// # Safety
///
/// See `thread::Builder::spawn_unchecked` for safety requirements.
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
let inner = Box::new(ThreadInner {
start: UnsafeCell::new(ManuallyDrop::new(p)),
lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/sgx/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ pub mod wait_notify {

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce() + Send>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce() + Send>,
) -> io::Result<Thread> {
let mut queue_lock = task_queue::lock();
unsafe { usercalls::launch_thread()? };
let (task, handle) = task_queue::Task::new(p);
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/teeos/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ unsafe extern "C" {

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
let p = Box::into_raw(Box::new(p));
let mut native: libc::pthread_t = unsafe { mem::zeroed() };
let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/uefi/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
_name: Option<&str>,
_p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
unsupported()
}

Expand Down
23 changes: 13 additions & 10 deletions library/std/src/sys/pal/unix/stack_overflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ pub struct Handler {
}

impl Handler {
pub unsafe fn new() -> Handler {
make_handler(false)
pub unsafe fn new(thread_name: Option<Box<str>>) -> Handler {
make_handler(false, thread_name)
}

fn null() -> Handler {
Expand Down Expand Up @@ -72,7 +72,6 @@ mod imp {
use crate::sync::OnceLock;
use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
use crate::sys::pal::unix::os;
use crate::thread::with_current_name;
use crate::{io, mem, panic, ptr};

// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
Expand Down Expand Up @@ -158,13 +157,12 @@ mod imp {
if !NEED_ALTSTACK.load(Ordering::Relaxed) {
// haven't set up our sigaltstack yet
NEED_ALTSTACK.store(true, Ordering::Release);
let handler = unsafe { make_handler(true) };
let handler = unsafe { make_handler(true, None) };
MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
mem::forget(handler);

if let Some(guard_page_range) = guard_page_range.take() {
let thread_name = with_current_name(|name| name.map(Box::from));
set_current_info(guard_page_range, thread_name);
set_current_info(guard_page_range, Some(Box::from("main")));
}
}

Expand Down Expand Up @@ -230,14 +228,13 @@ mod imp {
/// # Safety
/// Mutates the alternate signal stack
#[forbid(unsafe_op_in_unsafe_fn)]
pub unsafe fn make_handler(main_thread: bool) -> Handler {
pub unsafe fn make_handler(main_thread: bool, thread_name: Option<Box<str>>) -> Handler {
if !NEED_ALTSTACK.load(Ordering::Acquire) {
return Handler::null();
}

if !main_thread {
if let Some(guard_page_range) = unsafe { current_guard() } {
let thread_name = with_current_name(|name| name.map(Box::from));
set_current_info(guard_page_range, thread_name);
}
}
Expand Down Expand Up @@ -634,7 +631,10 @@ mod imp {

pub unsafe fn cleanup() {}

pub unsafe fn make_handler(_main_thread: bool) -> super::Handler {
pub unsafe fn make_handler(
_main_thread: bool,
_thread_name: Option<Box<str>>,
) -> super::Handler {
super::Handler::null()
}

Expand Down Expand Up @@ -717,7 +717,10 @@ mod imp {

pub unsafe fn cleanup() {}

pub unsafe fn make_handler(main_thread: bool) -> super::Handler {
pub unsafe fn make_handler(
main_thread: bool,
_thread_name: Option<Box<str>>,
) -> super::Handler {
if !main_thread {
reserve_stack();
}
Expand Down
24 changes: 17 additions & 7 deletions library/std/src/sys/pal/unix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
#[cfg(any(target_os = "espidf", target_os = "nuttx"))]
pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used

struct ThreadData {
name: Option<Box<str>>,
f: Box<dyn FnOnce()>,
}

pub struct Thread {
id: libc::pthread_t,
}
Expand All @@ -34,8 +39,12 @@ unsafe impl Sync for Thread {}
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
let p = Box::into_raw(Box::new(p));
pub unsafe fn new(
stack: usize,
name: Option<&str>,
f: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
let data = Box::into_raw(Box::new(ThreadData { name: name.map(Box::from), f }));
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
Expand Down Expand Up @@ -73,7 +82,7 @@ impl Thread {
};
}

let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, p as *mut _);
let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, data as *mut _);
// Note: if the thread creation fails and this assert fails, then p will
// be leaked. However, an alternative design could cause double-free
// which is clearly worse.
Expand All @@ -82,19 +91,20 @@ impl Thread {
return if ret != 0 {
// The thread failed to start and as a result p was not consumed. Therefore, it is
// safe to reconstruct the box so that it gets deallocated.
drop(Box::from_raw(p));
drop(Box::from_raw(data));
Err(io::Error::from_raw_os_error(ret))
} else {
Ok(Thread { id: native })
};

extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void {
unsafe {
let data = Box::from_raw(data as *mut ThreadData);
// Next, set up our stack overflow handler which may get triggered if we run
// out of stack.
let _handler = stack_overflow::Handler::new();
let _handler = stack_overflow::Handler::new(data.name);
// Finally, let's run some code.
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
(data.f)();
}
ptr::null_mut()
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/unsupported/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
_name: Option<&str>,
_p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
unsupported()
}

Expand Down
4 changes: 2 additions & 2 deletions library/std/src/sys/pal/wasi/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
let p = Box::into_raw(Box::new(p));
let mut native: libc::pthread_t = unsafe { mem::zeroed() };
let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
Expand Down Expand Up @@ -120,7 +120,7 @@ impl Thread {
}
}
} else {
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
crate::sys::unsupported()
}
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/wasm/atomics/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
_name: Option<&str>,
_p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
unsupported()
}

Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/windows/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ pub struct Thread {
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
let p = Box::into_raw(Box::new(p));

// CreateThread rounds up values for the stack size to the nearest page size (at least 4kb).
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/pal/xous/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ pub const GUARD_PAGE_SIZE: usize = 4096;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
_name: Option<&str>,
p: Box<dyn FnOnce()>,
) -> io::Result<Thread> {
let p = Box::into_raw(Box::new(p));
let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);

Expand Down
2 changes: 1 addition & 1 deletion library/std/src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ impl Builder {
// Similarly, the `sys` implementation must guarantee that no references to the closure
// exist after the thread has terminated, which is signaled by `Thread::join`
// returning.
native: unsafe { imp::Thread::new(stack_size, main)? },
native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? },
thread: my_thread,
packet: my_packet,
})
Expand Down
12 changes: 9 additions & 3 deletions tests/ui/runtime/out-of-stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extern crate libc;
use std::env;
use std::hint::black_box;
use std::process::Command;
use std::thread;
use std::thread::Builder;

fn silent_recurse() {
let buf = [0u8; 1000];
Expand Down Expand Up @@ -56,9 +56,9 @@ fn main() {
} else if args.len() > 1 && args[1] == "loud" {
loud_recurse();
} else if args.len() > 1 && args[1] == "silent-thread" {
thread::spawn(silent_recurse).join();
Builder::new().name("ferris".to_string()).spawn(silent_recurse).unwrap().join();
} else if args.len() > 1 && args[1] == "loud-thread" {
thread::spawn(loud_recurse).join();
Builder::new().name("ferris".to_string()).spawn(loud_recurse).unwrap().join();
} else {
let mut modes = vec![
"silent-thread",
Expand All @@ -82,6 +82,12 @@ fn main() {
let error = String::from_utf8_lossy(&silent.stderr);
assert!(error.contains("has overflowed its stack"),
"missing overflow message: {}", error);

if mode.contains("thread") {
assert!(error.contains("ferris"), "missing thread name: {}", error);
} else {
assert!(error.contains("main"), "missing thread name: {}", error);
}
}
}
}
Loading