rework threadname test for more consistency
This commit is contained in:
parent
a495a79db9
commit
b2b0d240a2
@ -1,9 +1,23 @@
|
|||||||
//@ignore-target: windows # No pthreads on Windows
|
//@ignore-target: windows # No pthreads on Windows
|
||||||
use std::ffi::CStr;
|
use std::ffi::{CStr, CString};
|
||||||
#[cfg(not(target_os = "freebsd"))]
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
const MAX_THREAD_NAME_LEN: usize = {
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "linux"))] {
|
||||||
|
16
|
||||||
|
} else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
|
||||||
|
32
|
||||||
|
} else if #[cfg(target_os = "macos")] {
|
||||||
|
libc::MAXTHREADNAMESIZE // 64, at the time of writing
|
||||||
|
} else if #[cfg(target_os = "freebsd")] {
|
||||||
|
usize::MAX // as far as I can tell
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// The short name should be shorter than 16 bytes which POSIX promises
|
// The short name should be shorter than 16 bytes which POSIX promises
|
||||||
// for thread names. The length includes a null terminator.
|
// for thread names. The length includes a null terminator.
|
||||||
@ -52,46 +66,64 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(short_name.clone())
|
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
// Rust remembers the full thread name itself.
|
// Set short thread name.
|
||||||
assert_eq!(thread::current().name(), Some(short_name.as_str()));
|
let cstr = CString::new(short_name.clone()).unwrap();
|
||||||
|
assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit
|
||||||
// Note that glibc requires 15 bytes long buffer exculding a null terminator.
|
|
||||||
// Otherwise, `pthread_getname_np` returns an error.
|
|
||||||
let mut buf = vec![0u8; short_name.len().max(15) + 1];
|
|
||||||
assert_eq!(get_thread_name(&mut buf), 0);
|
|
||||||
|
|
||||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
|
||||||
// POSIX seems to promise at least 15 chars excluding a null terminator.
|
|
||||||
assert_eq!(short_name.as_bytes(), cstr.to_bytes());
|
|
||||||
|
|
||||||
// Also test directly calling pthread_setname to check its return value.
|
|
||||||
assert_eq!(set_thread_name(&cstr), 0);
|
assert_eq!(set_thread_name(&cstr), 0);
|
||||||
|
|
||||||
|
// Now get it again, in various ways.
|
||||||
|
|
||||||
|
// POSIX seems to promise at least 15 chars excluding a null terminator.
|
||||||
|
let mut buf = vec![0u8; short_name.len().max(15) + 1];
|
||||||
|
assert_eq!(get_thread_name(&mut buf), 0);
|
||||||
|
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||||
|
assert_eq!(cstr.to_bytes(), short_name.as_bytes());
|
||||||
|
|
||||||
|
// Test what happens when the buffer is shorter than 16, but still long enough.
|
||||||
|
let res = get_thread_name(&mut buf[..15]);
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_os = "linux")] {
|
||||||
// For glibc used by linux-gnu there should be a failue,
|
// For glibc used by linux-gnu there should be a failue,
|
||||||
// if a shorter than 16 bytes buffer is provided, even if that would be
|
// if a shorter than 16 bytes buffer is provided, even if that would be
|
||||||
// large enough for the thread name.
|
// large enough for the thread name.
|
||||||
#[cfg(target_os = "linux")]
|
assert_eq!(res, libc::ERANGE);
|
||||||
assert_eq!(get_thread_name(&mut buf[..15]), libc::ERANGE);
|
} else {
|
||||||
|
// Everywhere else, this should work.
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
// POSIX seems to promise at least 15 chars excluding a null terminator.
|
||||||
|
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||||
|
assert_eq!(short_name.as_bytes(), cstr.to_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Solaris compatible implementations return an error,
|
// Test what happens when the buffer is too short even for the short name.
|
||||||
// if the buffer is shorter than the thread name.
|
let res = get_thread_name(&mut buf[..4]);
|
||||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
cfg_if::cfg_if! {
|
||||||
assert_eq!(get_thread_name(&mut buf[..4]), libc::ERANGE);
|
if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
|
||||||
// On macOS and FreeBSD it's not an error for the buffer to be
|
// On macOS and FreeBSD it's not an error for the buffer to be
|
||||||
// too short for the thread name -- they truncate instead.
|
// too short for the thread name -- they truncate instead.
|
||||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
assert_eq!(res, 0);
|
||||||
{
|
|
||||||
// Ensure that a zero sized buffer returns no error.
|
|
||||||
assert_eq!(get_thread_name(&mut buf[..0]), 0);
|
|
||||||
|
|
||||||
// Ensure that a shorter tnan required buffer still returns no error,
|
|
||||||
// and gives a prefix of the thread name.
|
|
||||||
assert_eq!(get_thread_name(&mut buf[..4]), 0);
|
|
||||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||||
assert_eq!(cstr.to_bytes_with_nul().len(), 4);
|
assert_eq!(cstr.to_bytes_with_nul().len(), 4);
|
||||||
assert!(short_name.as_bytes().starts_with(cstr.to_bytes()));
|
assert!(short_name.as_bytes().starts_with(cstr.to_bytes()));
|
||||||
|
} else {
|
||||||
|
// The rest should give an error.
|
||||||
|
assert_eq!(res, libc::ERANGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test zero-sized buffer.
|
||||||
|
let res = get_thread_name(&mut []);
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
|
||||||
|
// On macOS and FreeBSD it's not an error for the buffer to be
|
||||||
|
// too short for the thread name -- even with size 0.
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
} else {
|
||||||
|
// The rest should give an error.
|
||||||
|
assert_eq!(res, libc::ERANGE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -99,37 +131,52 @@ fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(long_name.clone())
|
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
// Rust remembers the full thread name itself.
|
// Set full thread name.
|
||||||
assert_eq!(thread::current().name(), Some(long_name.as_str()));
|
let cstr = CString::new(long_name.clone()).unwrap();
|
||||||
|
let res = set_thread_name(&cstr);
|
||||||
// But the system is limited -- make sure we successfully set a truncation.
|
cfg_if::cfg_if! {
|
||||||
// Note that there's no specific to glibc buffer requirement, since the value
|
if #[cfg(target_os = "freebsd")] {
|
||||||
// `long_name` is longer than 16 bytes including a null terminator.
|
// Names of all size are supported.
|
||||||
let mut buf = vec![0u8; long_name.len() + 1];
|
assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN);
|
||||||
assert_eq!(get_thread_name(&mut buf), 0);
|
assert_eq!(res, 0);
|
||||||
|
} else if #[cfg(target_os = "macos")] {
|
||||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
// Name is too long.
|
||||||
// POSIX seems to promise at least 15 chars excluding a null terminator.
|
assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
|
||||||
assert!(
|
assert_eq!(res, libc::ENAMETOOLONG);
|
||||||
cstr.to_bytes().len() >= 15,
|
} else {
|
||||||
"name is too short: len={}",
|
// Name is too long.
|
||||||
cstr.to_bytes().len()
|
assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
|
||||||
);
|
assert_eq!(res, libc::ERANGE);
|
||||||
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
|
}
|
||||||
|
}
|
||||||
// Also test directly calling pthread_setname to check its return value.
|
// Set the longest name we can.
|
||||||
|
let truncated_name = &long_name[..long_name.len().min(MAX_THREAD_NAME_LEN - 1)];
|
||||||
|
let cstr = CString::new(truncated_name).unwrap();
|
||||||
assert_eq!(set_thread_name(&cstr), 0);
|
assert_eq!(set_thread_name(&cstr), 0);
|
||||||
|
|
||||||
// But with a too long name it should fail (except on FreeBSD where
|
// Now get it again, in various ways.
|
||||||
// names of arbitrary size seem to be supported).
|
|
||||||
// On macOS, the error code is different.
|
|
||||||
#[cfg(not(any(target_os = "freebsd", target_os = "macos")))]
|
|
||||||
assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ERANGE);
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
// This name should round-trip properly.
|
||||||
assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ENAMETOOLONG);
|
let mut buf = vec![0u8; long_name.len() + 1];
|
||||||
|
assert_eq!(get_thread_name(&mut buf), 0);
|
||||||
|
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||||
|
assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
|
||||||
|
|
||||||
|
// Test what happens when our buffer is just one byte too small.
|
||||||
|
let res = get_thread_name(&mut buf[..truncated_name.len()]);
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
|
||||||
|
// On macOS and FreeBSD it's not an error for the buffer to be
|
||||||
|
// too short for the thread name -- they truncate instead.
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||||
|
assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]);
|
||||||
|
} else {
|
||||||
|
// The rest should give an error.
|
||||||
|
assert_eq!(res, libc::ERANGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.join()
|
.join()
|
||||||
|
@ -16,6 +16,19 @@ fn main() {
|
|||||||
.join()
|
.join()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Long thread name.
|
||||||
|
let long_name = std::iter::once("test_named_thread_truncation")
|
||||||
|
.chain(std::iter::repeat(" long").take(100))
|
||||||
|
.collect::<String>();
|
||||||
|
thread::Builder::new()
|
||||||
|
.name(long_name.clone())
|
||||||
|
.spawn(move || {
|
||||||
|
assert_eq!(thread::current().name().unwrap(), long_name);
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Also check main thread name.
|
// Also check main thread name.
|
||||||
assert_eq!(thread::current().name().unwrap(), "main");
|
assert_eq!(thread::current().name().unwrap(), "main");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user