diff options
| author | Ralf Jung <post@ralfj.de> | 2024-10-13 09:13:33 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2024-10-13 09:13:33 +0200 |
| commit | b2b0d240a284567c27da2fe6015128bc8e506441 (patch) | |
| tree | 35cf79d707e875c6dfb9a9cbc99860f81775741a | |
| parent | a495a79db95c15071eb787359feaf71424321dd0 (diff) | |
| download | rust-b2b0d240a284567c27da2fe6015128bc8e506441.tar.gz rust-b2b0d240a284567c27da2fe6015128bc8e506441.zip | |
rework threadname test for more consistency
| -rw-r--r-- | src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs | 171 | ||||
| -rw-r--r-- | src/tools/miri/tests/pass/concurrency/threadname.rs | 13 |
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"); } |
