Skip to content

Commit

Permalink
Rollup merge of #103379 - cuviper:truncate-thread-name, r=thomcc
Browse files Browse the repository at this point in the history
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
Dylan-DPC authored Oct 24, 2022
2 parents 60f3f07 + 15cfeb3 commit 64e66e1
Show file tree
Hide file tree
Showing 2 changed files with 49 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
31 changes: 31 additions & 0 deletions library/std/src/thread/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,37 @@ fn test_named_thread() {
.unwrap();
}

#[cfg(any(
// Note: musl didn't add pthread_getname_np until 1.2.3
all(target_os = "linux", target_env = "gnu"),
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 64e66e1

Please sign in to comment.