Skip to content

Commit

Permalink
Rollup merge of rust-lang#103379 - cuviper:truncate-thread-name, r=th…
Browse files Browse the repository at this point in the history
…omcc

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.

r? ``@thomcc``
  • Loading branch information
notriddle committed Oct 22, 2022
2 parents 0ab4934 + 12e4584 commit dcfb161
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
18 changes: 18 additions & 0 deletions library/std/src/sys/unix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,11 @@ impl Thread {

#[cfg(target_os = "linux")]
pub fn set_name(name: &CStr) {
const TASK_COMM_LEN: usize = 16;

unsafe {
// 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());
}
}
Expand All @@ -148,6 +151,7 @@ impl Thread {
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
pub fn set_name(name: &CStr) {
unsafe {
let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE);
libc::pthread_setname_np(name.as_ptr());
}
}
Expand Down Expand Up @@ -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> {
cfg_if::cfg_if! {
if #[cfg(any(
Expand Down
25 changes: 25 additions & 0 deletions library/std/src/thread/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,31 @@ fn test_named_thread() {
.unwrap();
}

#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
#[test]
fn test_named_thread_truncation() {
use crate::ffi::CStr;

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

#[test]
#[should_panic]
fn test_invalid_named_thread() {
Expand Down

0 comments on commit dcfb161

Please sign in to comment.