thread name in stack overflow message

This commit is contained in:
joboet 2025-07-26 14:29:00 +02:00
parent 051d0e8a95
commit 73751a0491
No known key found for this signature in database
GPG key ID: 704E0149B0194B3C
14 changed files with 87 additions and 32 deletions

View file

@ -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 */)
}

View file

@ -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),

View file

@ -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);

View file

@ -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() };

View file

@ -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()
}

View file

@ -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 {
@ -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
@ -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")));
}
}
@ -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);
}
}
@ -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()
}
@ -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();
}

View file

@ -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,
}
@ -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);
@ -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.
@ -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()
}

View file

@ -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()
}

View file

@ -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() };
@ -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()
}
}

View file

@ -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()
}

View file

@ -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).

View file

@ -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);

View file

@ -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,
})

View file

@ -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];
@ -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",
@ -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);
}
}
}
}