Skip to content

Commit d901b16

Browse files
authored
Rollup merge of rust-lang#103379 - cuviper:truncate-thread-name, r=thomcc
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`
2 parents afab9f1 + 12e4584 commit d901b16

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

library/std/src/sys/unix/thread.rs

+18
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,11 @@ impl Thread {
132132

133133
#[cfg(target_os = "linux")]
134134
pub fn set_name(name: &CStr) {
135+
const TASK_COMM_LEN: usize = 16;
136+
135137
unsafe {
136138
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
139+
let name = truncate_cstr(name, TASK_COMM_LEN);
137140
libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
138141
}
139142
}
@@ -148,6 +151,7 @@ impl Thread {
148151
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
149152
pub fn set_name(name: &CStr) {
150153
unsafe {
154+
let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE);
151155
libc::pthread_setname_np(name.as_ptr());
152156
}
153157
}
@@ -276,6 +280,20 @@ impl Drop for Thread {
276280
}
277281
}
278282

283+
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
284+
fn truncate_cstr(cstr: &CStr, max_with_nul: usize) -> crate::borrow::Cow<'_, CStr> {
285+
use crate::{borrow::Cow, ffi::CString};
286+
287+
if cstr.to_bytes_with_nul().len() > max_with_nul {
288+
let bytes = cstr.to_bytes()[..max_with_nul - 1].to_vec();
289+
// SAFETY: the non-nul bytes came straight from a CStr.
290+
// (CString will add the terminating nul.)
291+
Cow::Owned(unsafe { CString::from_vec_unchecked(bytes) })
292+
} else {
293+
Cow::Borrowed(cstr)
294+
}
295+
}
296+
279297
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
280298
cfg_if::cfg_if! {
281299
if #[cfg(any(

library/std/src/thread/tests.rs

+25
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,31 @@ fn test_named_thread() {
3737
.unwrap();
3838
}
3939

40+
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
41+
#[test]
42+
fn test_named_thread_truncation() {
43+
use crate::ffi::CStr;
44+
45+
let long_name = crate::iter::once("test_named_thread_truncation")
46+
.chain(crate::iter::repeat(" yada").take(100))
47+
.collect::<String>();
48+
49+
let result = Builder::new().name(long_name.clone()).spawn(move || {
50+
// Rust remembers the full thread name itself.
51+
assert_eq!(thread::current().name(), Some(long_name.as_str()));
52+
53+
// But the system is limited -- make sure we successfully set a truncation.
54+
let mut buf = vec![0u8; long_name.len() + 1];
55+
unsafe {
56+
libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len());
57+
}
58+
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
59+
assert!(cstr.to_bytes().len() > 0);
60+
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
61+
});
62+
result.unwrap().join().unwrap();
63+
}
64+
4065
#[test]
4166
#[should_panic]
4267
fn test_invalid_named_thread() {

0 commit comments

Comments
 (0)