about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-10-13 09:13:33 +0200
committerRalf Jung <post@ralfj.de>2024-10-13 09:13:33 +0200
commitb2b0d240a284567c27da2fe6015128bc8e506441 (patch)
tree35cf79d707e875c6dfb9a9cbc99860f81775741a
parenta495a79db95c15071eb787359feaf71424321dd0 (diff)
downloadrust-b2b0d240a284567c27da2fe6015128bc8e506441.tar.gz
rust-b2b0d240a284567c27da2fe6015128bc8e506441.zip
rework threadname test for more consistency
-rw-r--r--src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs171
-rw-r--r--src/tools/miri/tests/pass/concurrency/threadname.rs13
2 files changed, 122 insertions, 62 deletions
diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
index 74b15466202..404ef7cc42d 100644
--- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
+++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
@@ -1,9 +1,23 @@
 //@ignore-target: windows # No pthreads on Windows
-use std::ffi::CStr;
-#[cfg(not(target_os = "freebsd"))]
-use std::ffi::CString;
+use std::ffi::{CStr, CString};
 use std::thread;
 
+const MAX_THREAD_NAME_LEN: usize = {
+    cfg_if::cfg_if! {
+        if #[cfg(any(target_os = "linux"))] {
+            16
+        } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
+            32
+        } else if #[cfg(target_os = "macos")] {
+            libc::MAXTHREADNAMESIZE // 64, at the time of writing
+        } else if #[cfg(target_os = "freebsd")] {
+            usize::MAX // as far as I can tell
+        } else {
+            panic!()
+        }
+    }
+};
+
 fn main() {
     // The short name should be shorter than 16 bytes which POSIX promises
     // for thread names. The length includes a null terminator.
@@ -52,46 +66,64 @@ fn main() {
     }
 
     thread::Builder::new()
-        .name(short_name.clone())
         .spawn(move || {
-            // Rust remembers the full thread name itself.
-            assert_eq!(thread::current().name(), Some(short_name.as_str()));
+            // Set short thread name.
+            let cstr = CString::new(short_name.clone()).unwrap();
+            assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit
+            assert_eq!(set_thread_name(&cstr), 0);
 
-            // Note that glibc requires 15 bytes long buffer exculding a null terminator.
-            // Otherwise, `pthread_getname_np` returns an error.
+            // Now get it again, in various ways.
+
+            // POSIX seems to promise at least 15 chars excluding a null terminator.
             let mut buf = vec![0u8; short_name.len().max(15) + 1];
             assert_eq!(get_thread_name(&mut buf), 0);
-
             let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
-            // POSIX seems to promise at least 15 chars excluding a null terminator.
-            assert_eq!(short_name.as_bytes(), cstr.to_bytes());
-
-            // Also test directly calling pthread_setname to check its return value.
-            assert_eq!(set_thread_name(&cstr), 0);
-
-            // For glibc used by linux-gnu there should be a failue,
-            // if a shorter than 16 bytes buffer is provided, even if that would be
-            // large enough for the thread name.
-            #[cfg(target_os = "linux")]
-            assert_eq!(get_thread_name(&mut buf[..15]), libc::ERANGE);
+            assert_eq!(cstr.to_bytes(), short_name.as_bytes());
+
+            // Test what happens when the buffer is shorter than 16, but still long enough.
+            let res = get_thread_name(&mut buf[..15]);
+            cfg_if::cfg_if! {
+                if #[cfg(target_os = "linux")] {
+                    // For glibc used by linux-gnu there should be a failue,
+                    // if a shorter than 16 bytes buffer is provided, even if that would be
+                    // large enough for the thread name.
+                    assert_eq!(res, libc::ERANGE);
+                } else {
+                    // Everywhere else, this should work.
+                    assert_eq!(res, 0);
+                    // POSIX seems to promise at least 15 chars excluding a null terminator.
+                    let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+                    assert_eq!(short_name.as_bytes(), cstr.to_bytes());
+                }
+            }
 
-            // Solaris compatible implementations return an error,
-            // if the buffer is shorter than the thread name.
-            #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-            assert_eq!(get_thread_name(&mut buf[..4]), libc::ERANGE);
-            // On macOS and FreeBSD it's not an error for the buffer to be
-            // too short for the thread name -- they truncate instead.
-            #[cfg(any(target_os = "freebsd", target_os = "macos"))]
-            {
-                // Ensure that a zero sized buffer returns no error.
-                assert_eq!(get_thread_name(&mut buf[..0]), 0);
+            // Test what happens when the buffer is too short even for the short name.
+            let res = get_thread_name(&mut buf[..4]);
+            cfg_if::cfg_if! {
+                if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
+                    // On macOS and FreeBSD it's not an error for the buffer to be
+                    // too short for the thread name -- they truncate instead.
+                    assert_eq!(res, 0);
+                    let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+                    assert_eq!(cstr.to_bytes_with_nul().len(), 4);
+                    assert!(short_name.as_bytes().starts_with(cstr.to_bytes()));
+                } else {
+                    // The rest should give an error.
+                    assert_eq!(res, libc::ERANGE);
+                }
+            }
 
-                // Ensure that a shorter tnan required buffer still returns no error,
-                // and gives a prefix of the thread name.
-                assert_eq!(get_thread_name(&mut buf[..4]), 0);
-                let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
-                assert_eq!(cstr.to_bytes_with_nul().len(), 4);
-                assert!(short_name.as_bytes().starts_with(cstr.to_bytes()));
+            // Test zero-sized buffer.
+            let res = get_thread_name(&mut []);
+            cfg_if::cfg_if! {
+                if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
+                    // On macOS and FreeBSD it's not an error for the buffer to be
+                    // too short for the thread name -- even with size 0.
+                    assert_eq!(res, 0);
+                } else {
+                    // The rest should give an error.
+                    assert_eq!(res, libc::ERANGE);
+                }
             }
         })
         .unwrap()
@@ -99,37 +131,52 @@ fn main() {
         .unwrap();
 
     thread::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()));
+            // Set full thread name.
+            let cstr = CString::new(long_name.clone()).unwrap();
+            let res = set_thread_name(&cstr);
+            cfg_if::cfg_if! {
+                if #[cfg(target_os = "freebsd")] {
+                    // Names of all size are supported.
+                    assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN);
+                    assert_eq!(res, 0);
+                } else if #[cfg(target_os = "macos")] {
+                    // Name is too long.
+                    assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
+                    assert_eq!(res, libc::ENAMETOOLONG);
+                } else {
+                    // Name is too long.
+                    assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
+                    assert_eq!(res, libc::ERANGE);
+                }
+            }
+            // Set the longest name we can.
+            let truncated_name = &long_name[..long_name.len().min(MAX_THREAD_NAME_LEN - 1)];
+            let cstr = CString::new(truncated_name).unwrap();
+            assert_eq!(set_thread_name(&cstr), 0);
+
+            // Now get it again, in various ways.
 
-            // But the system is limited -- make sure we successfully set a truncation.
-            // Note that there's no specific to glibc buffer requirement, since the value
-            // `long_name` is longer than 16 bytes including a null terminator.
+            // This name should round-trip properly.
             let mut buf = vec![0u8; long_name.len() + 1];
             assert_eq!(get_thread_name(&mut buf), 0);
-
             let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
-            // POSIX seems to promise at least 15 chars excluding a null terminator.
-            assert!(
-                cstr.to_bytes().len() >= 15,
-                "name is too short: len={}",
-                cstr.to_bytes().len()
-            );
-            assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
-
-            // Also test directly calling pthread_setname to check its return value.
-            assert_eq!(set_thread_name(&cstr), 0);
-
-            // But with a too long name it should fail (except on FreeBSD where
-            // names of arbitrary size seem to be supported).
-            // On macOS, the error code is different.
-            #[cfg(not(any(target_os = "freebsd", target_os = "macos")))]
-            assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ERANGE);
-
-            #[cfg(target_os = "macos")]
-            assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ENAMETOOLONG);
+            assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
+
+            // Test what happens when our buffer is just one byte too small.
+            let res = get_thread_name(&mut buf[..truncated_name.len()]);
+            cfg_if::cfg_if! {
+                if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
+                    // On macOS and FreeBSD it's not an error for the buffer to be
+                    // too short for the thread name -- they truncate instead.
+                    assert_eq!(res, 0);
+                    let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+                    assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]);
+                } else {
+                    // The rest should give an error.
+                    assert_eq!(res, libc::ERANGE);
+                }
+            }
         })
         .unwrap()
         .join()
diff --git a/src/tools/miri/tests/pass/concurrency/threadname.rs b/src/tools/miri/tests/pass/concurrency/threadname.rs
index 6dd5f1f5c91..41cac6459b2 100644
--- a/src/tools/miri/tests/pass/concurrency/threadname.rs
+++ b/src/tools/miri/tests/pass/concurrency/threadname.rs
@@ -16,6 +16,19 @@ fn main() {
         .join()
         .unwrap();
 
+    // Long thread name.
+    let long_name = std::iter::once("test_named_thread_truncation")
+        .chain(std::iter::repeat(" long").take(100))
+        .collect::<String>();
+    thread::Builder::new()
+        .name(long_name.clone())
+        .spawn(move || {
+            assert_eq!(thread::current().name().unwrap(), long_name);
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+
     // Also check main thread name.
     assert_eq!(thread::current().name().unwrap(), "main");
 }