|
1 | 1 | //@ignore-target: windows # No pthreads on Windows
|
2 |
| -use std::ffi::CStr; |
3 |
| -#[cfg(not(target_os = "freebsd"))] |
4 |
| -use std::ffi::CString; |
| 2 | +use std::ffi::{CStr, CString}; |
5 | 3 | use std::thread;
|
6 | 4 |
|
| 5 | +const MAX_THREAD_NAME_LEN: usize = { |
| 6 | + cfg_if::cfg_if! { |
| 7 | + if #[cfg(any(target_os = "linux"))] { |
| 8 | + 16 |
| 9 | + } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { |
| 10 | + 32 |
| 11 | + } else if #[cfg(target_os = "macos")] { |
| 12 | + libc::MAXTHREADNAMESIZE // 64, at the time of writing |
| 13 | + } else if #[cfg(target_os = "freebsd")] { |
| 14 | + usize::MAX // as far as I can tell |
| 15 | + } else { |
| 16 | + panic!() |
| 17 | + } |
| 18 | + } |
| 19 | +}; |
| 20 | + |
7 | 21 | fn main() {
|
8 | 22 | // The short name should be shorter than 16 bytes which POSIX promises
|
9 | 23 | // for thread names. The length includes a null terminator.
|
@@ -52,61 +66,117 @@ fn main() {
|
52 | 66 | }
|
53 | 67 |
|
54 | 68 | thread::Builder::new()
|
55 |
| - .name(short_name.clone()) |
56 | 69 | .spawn(move || {
|
57 |
| - // Rust remembers the full thread name itself. |
58 |
| - assert_eq!(thread::current().name(), Some(short_name.as_str())); |
| 70 | + // Set short thread name. |
| 71 | + let cstr = CString::new(short_name.clone()).unwrap(); |
| 72 | + assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit |
| 73 | + assert_eq!(set_thread_name(&cstr), 0); |
59 | 74 |
|
60 |
| - // Note that glibc requires 15 bytes long buffer exculding a null terminator. |
61 |
| - // Otherwise, `pthread_getname_np` returns an error. |
| 75 | + // Now get it again, in various ways. |
| 76 | + |
| 77 | + // POSIX seems to promise at least 15 chars excluding a null terminator. |
62 | 78 | let mut buf = vec![0u8; short_name.len().max(15) + 1];
|
63 | 79 | assert_eq!(get_thread_name(&mut buf), 0);
|
64 |
| - |
65 | 80 | let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
66 |
| - // POSIX seems to promise at least 15 chars excluding a null terminator. |
67 |
| - assert_eq!(short_name.as_bytes(), cstr.to_bytes()); |
| 81 | + assert_eq!(cstr.to_bytes(), short_name.as_bytes()); |
68 | 82 |
|
69 |
| - // Also test directly calling pthread_setname to check its return value. |
70 |
| - assert_eq!(set_thread_name(&cstr), 0); |
| 83 | + // Test what happens when the buffer is shorter than 16, but still long enough. |
| 84 | + let res = get_thread_name(&mut buf[..15]); |
| 85 | + cfg_if::cfg_if! { |
| 86 | + if #[cfg(target_os = "linux")] { |
| 87 | + // For glibc used by linux-gnu there should be a failue, |
| 88 | + // if a shorter than 16 bytes buffer is provided, even if that would be |
| 89 | + // large enough for the thread name. |
| 90 | + assert_eq!(res, libc::ERANGE); |
| 91 | + } else { |
| 92 | + // Everywhere else, this should work. |
| 93 | + assert_eq!(res, 0); |
| 94 | + // POSIX seems to promise at least 15 chars excluding a null terminator. |
| 95 | + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); |
| 96 | + assert_eq!(short_name.as_bytes(), cstr.to_bytes()); |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + // Test what happens when the buffer is too short even for the short name. |
| 101 | + let res = get_thread_name(&mut buf[..4]); |
| 102 | + cfg_if::cfg_if! { |
| 103 | + if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { |
| 104 | + // On macOS and FreeBSD it's not an error for the buffer to be |
| 105 | + // too short for the thread name -- they truncate instead. |
| 106 | + assert_eq!(res, 0); |
| 107 | + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); |
| 108 | + assert_eq!(cstr.to_bytes_with_nul().len(), 4); |
| 109 | + assert!(short_name.as_bytes().starts_with(cstr.to_bytes())); |
| 110 | + } else { |
| 111 | + // The rest should give an error. |
| 112 | + assert_eq!(res, libc::ERANGE); |
| 113 | + } |
| 114 | + } |
71 | 115 |
|
72 |
| - // For glibc used by linux-gnu there should be a failue, |
73 |
| - // if a shorter than 16 bytes buffer is provided, even if that would be |
74 |
| - // large enough for the thread name. |
75 |
| - #[cfg(target_os = "linux")] |
76 |
| - assert_eq!(get_thread_name(&mut buf[..15]), libc::ERANGE); |
| 116 | + // Test zero-sized buffer. |
| 117 | + let res = get_thread_name(&mut []); |
| 118 | + cfg_if::cfg_if! { |
| 119 | + if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { |
| 120 | + // On macOS and FreeBSD it's not an error for the buffer to be |
| 121 | + // too short for the thread name -- even with size 0. |
| 122 | + assert_eq!(res, 0); |
| 123 | + } else { |
| 124 | + // The rest should give an error. |
| 125 | + assert_eq!(res, libc::ERANGE); |
| 126 | + } |
| 127 | + } |
77 | 128 | })
|
78 | 129 | .unwrap()
|
79 | 130 | .join()
|
80 | 131 | .unwrap();
|
81 | 132 |
|
82 | 133 | thread::Builder::new()
|
83 |
| - .name(long_name.clone()) |
84 | 134 | .spawn(move || {
|
85 |
| - // Rust remembers the full thread name itself. |
86 |
| - assert_eq!(thread::current().name(), Some(long_name.as_str())); |
| 135 | + // Set full thread name. |
| 136 | + let cstr = CString::new(long_name.clone()).unwrap(); |
| 137 | + let res = set_thread_name(&cstr); |
| 138 | + cfg_if::cfg_if! { |
| 139 | + if #[cfg(target_os = "freebsd")] { |
| 140 | + // Names of all size are supported. |
| 141 | + assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); |
| 142 | + assert_eq!(res, 0); |
| 143 | + } else if #[cfg(target_os = "macos")] { |
| 144 | + // Name is too long. |
| 145 | + assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); |
| 146 | + assert_eq!(res, libc::ENAMETOOLONG); |
| 147 | + } else { |
| 148 | + // Name is too long. |
| 149 | + assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); |
| 150 | + assert_eq!(res, libc::ERANGE); |
| 151 | + } |
| 152 | + } |
| 153 | + // Set the longest name we can. |
| 154 | + let truncated_name = &long_name[..long_name.len().min(MAX_THREAD_NAME_LEN - 1)]; |
| 155 | + let cstr = CString::new(truncated_name).unwrap(); |
| 156 | + assert_eq!(set_thread_name(&cstr), 0); |
| 157 | + |
| 158 | + // Now get it again, in various ways. |
87 | 159 |
|
88 |
| - // But the system is limited -- make sure we successfully set a truncation. |
89 |
| - // Note that there's no specific to glibc buffer requirement, since the value |
90 |
| - // `long_name` is longer than 16 bytes including a null terminator. |
| 160 | + // This name should round-trip properly. |
91 | 161 | let mut buf = vec![0u8; long_name.len() + 1];
|
92 | 162 | assert_eq!(get_thread_name(&mut buf), 0);
|
93 |
| - |
94 | 163 | let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
95 |
| - // POSIX seems to promise at least 15 chars excluding a null terminator. |
96 |
| - assert!( |
97 |
| - cstr.to_bytes().len() >= 15, |
98 |
| - "name is too short: len={}", |
99 |
| - cstr.to_bytes().len() |
100 |
| - ); |
101 |
| - assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); |
102 |
| - |
103 |
| - // Also test directly calling pthread_setname to check its return value. |
104 |
| - assert_eq!(set_thread_name(&cstr), 0); |
| 164 | + assert_eq!(cstr.to_bytes(), truncated_name.as_bytes()); |
105 | 165 |
|
106 |
| - // But with a too long name it should fail (except on FreeBSD where the |
107 |
| - // function has no return, hence cannot indicate failure). |
108 |
| - #[cfg(not(target_os = "freebsd"))] |
109 |
| - assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0); |
| 166 | + // Test what happens when our buffer is just one byte too small. |
| 167 | + let res = get_thread_name(&mut buf[..truncated_name.len()]); |
| 168 | + cfg_if::cfg_if! { |
| 169 | + if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { |
| 170 | + // On macOS and FreeBSD it's not an error for the buffer to be |
| 171 | + // too short for the thread name -- they truncate instead. |
| 172 | + assert_eq!(res, 0); |
| 173 | + let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); |
| 174 | + assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]); |
| 175 | + } else { |
| 176 | + // The rest should give an error. |
| 177 | + assert_eq!(res, libc::ERANGE); |
| 178 | + } |
| 179 | + } |
110 | 180 | })
|
111 | 181 | .unwrap()
|
112 | 182 | .join()
|
|
0 commit comments