about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorJosh Stone <jistone@redhat.com>2022-10-21 17:44:35 -0700
committerJosh Stone <jistone@redhat.com>2022-10-21 17:44:35 -0700
commit7280f3d28aa139cec0c75072a3e66294b7f99b59 (patch)
treee70a206bd8325936ae51c36d28f54ae5c4029435 /library/std/src/sys
parent57e2c06a8df3187980801962015a88657abd033d (diff)
downloadrust-7280f3d28aa139cec0c75072a3e66294b7f99b59.tar.gz
rust-7280f3d28aa139cec0c75072a3e66294b7f99b59.zip
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.
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/unix/thread.rs43
1 files changed, 43 insertions, 0 deletions
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 42ac6fcd8bf..6a533854fad 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -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());
         }
     }
@@ -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());
         }
     }
@@ -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(
@@ -902,3 +920,28 @@ fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
     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();
+}