Truncate thread names on Linux and Apple targets
These targets have system limits on the thread names, 16 and 64 bytes respectively, and `pthread_setname_np` returns an error if the name is longer. However, we're not in a context that can propagate errors when we call this, and we used to implicitly truncate on Linux with `prctl`, so now we manually truncate these names ahead of time.
This commit is contained in:
parent
57e2c06a8d
commit
7280f3d28a
@ -132,8 +132,11 @@ impl Thread {
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn set_name(name: &CStr) {
|
pub fn set_name(name: &CStr) {
|
||||||
|
const TASK_COMM_LEN: usize = 16;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
|
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
|
||||||
|
let name = truncate_cstr(name, TASK_COMM_LEN);
|
||||||
libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
|
libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +151,7 @@ impl Thread {
|
|||||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
|
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
|
||||||
pub fn set_name(name: &CStr) {
|
pub fn set_name(name: &CStr) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE);
|
||||||
libc::pthread_setname_np(name.as_ptr());
|
libc::pthread_setname_np(name.as_ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,6 +280,20 @@ impl Drop for Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
|
||||||
|
fn truncate_cstr(cstr: &CStr, max_with_nul: usize) -> crate::borrow::Cow<'_, CStr> {
|
||||||
|
use crate::{borrow::Cow, ffi::CString};
|
||||||
|
|
||||||
|
if cstr.to_bytes_with_nul().len() > max_with_nul {
|
||||||
|
let bytes = cstr.to_bytes()[..max_with_nul - 1].to_vec();
|
||||||
|
// SAFETY: the non-nul bytes came straight from a CStr.
|
||||||
|
// (CString will add the terminating nul.)
|
||||||
|
Cow::Owned(unsafe { CString::from_vec_unchecked(bytes) })
|
||||||
|
} else {
|
||||||
|
Cow::Borrowed(cstr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
|
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(any(
|
if #[cfg(any(
|
||||||
@ -902,3 +920,28 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
|
|||||||
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
|
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
|
||||||
2048 // just a guess
|
2048 // just a guess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
|
||||||
|
fn test_named_thread_truncation() {
|
||||||
|
use crate::thread::{self, Builder};
|
||||||
|
|
||||||
|
let long_name = crate::iter::once("test_named_thread_truncation")
|
||||||
|
.chain(crate::iter::repeat(" yada").take(100))
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
let result = Builder::new().name(long_name.clone()).spawn(move || {
|
||||||
|
// Rust remembers the full thread name itself.
|
||||||
|
assert_eq!(thread::current().name(), Some(long_name.as_str()));
|
||||||
|
|
||||||
|
// But the kernel is limited -- make sure we successfully set a truncation.
|
||||||
|
let mut buf = vec![0u8; long_name.len() + 1];
|
||||||
|
unsafe {
|
||||||
|
libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len());
|
||||||
|
}
|
||||||
|
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||||
|
assert!(cstr.to_bytes().len() > 0);
|
||||||
|
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
|
||||||
|
});
|
||||||
|
result.unwrap().join().unwrap();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user