|
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,84 +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()); |
68 |
| - |
69 |
| - // Also test directly calling pthread_setname to check its return value. |
70 |
| - assert_eq!(set_thread_name(&cstr), 0); |
71 |
| - |
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); |
| 81 | + assert_eq!(cstr.to_bytes(), short_name.as_bytes()); |
| 82 | + |
| 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 | + } |
77 | 99 |
|
78 |
| - // Solaris compatible implementations return an error, |
79 |
| - // if the buffer is shorter than the thread name. |
80 |
| - #[cfg(any(target_os = "illumos", target_os = "solaris"))] |
81 |
| - assert_eq!(get_thread_name(&mut buf[..4]), libc::ERANGE); |
82 |
| - // On macOS and FreeBSD it's not an error for the buffer to be |
83 |
| - // too short for the thread name -- they truncate instead. |
84 |
| - #[cfg(any(target_os = "freebsd", target_os = "macos"))] |
85 |
| - { |
86 |
| - // Ensure that a zero sized buffer returns no error. |
87 |
| - assert_eq!(get_thread_name(&mut buf[..0]), 0); |
| 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 | + } |
88 | 115 |
|
89 |
| - // Ensure that a shorter tnan required buffer still returns no error, |
90 |
| - // and gives a prefix of the thread name. |
91 |
| - assert_eq!(get_thread_name(&mut buf[..4]), 0); |
92 |
| - let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); |
93 |
| - assert_eq!(cstr.to_bytes_with_nul().len(), 4); |
94 |
| - assert!(short_name.as_bytes().starts_with(cstr.to_bytes())); |
| 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 | + } |
95 | 127 | }
|
96 | 128 | })
|
97 | 129 | .unwrap()
|
98 | 130 | .join()
|
99 | 131 | .unwrap();
|
100 | 132 |
|
101 | 133 | thread::Builder::new()
|
102 |
| - .name(long_name.clone()) |
103 | 134 | .spawn(move || {
|
104 |
| - // Rust remembers the full thread name itself. |
105 |
| - 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. |
106 | 159 |
|
107 |
| - // But the system is limited -- make sure we successfully set a truncation. |
108 |
| - // Note that there's no specific to glibc buffer requirement, since the value |
109 |
| - // `long_name` is longer than 16 bytes including a null terminator. |
| 160 | + // This name should round-trip properly. |
110 | 161 | let mut buf = vec![0u8; long_name.len() + 1];
|
111 | 162 | assert_eq!(get_thread_name(&mut buf), 0);
|
112 |
| - |
113 | 163 | let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
114 |
| - // POSIX seems to promise at least 15 chars excluding a null terminator. |
115 |
| - assert!( |
116 |
| - cstr.to_bytes().len() >= 15, |
117 |
| - "name is too short: len={}", |
118 |
| - cstr.to_bytes().len() |
119 |
| - ); |
120 |
| - assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); |
121 |
| - |
122 |
| - // Also test directly calling pthread_setname to check its return value. |
123 |
| - assert_eq!(set_thread_name(&cstr), 0); |
124 |
| - |
125 |
| - // But with a too long name it should fail (except on FreeBSD where |
126 |
| - // names of arbitrary size seem to be supported). |
127 |
| - // On macOS, the error code is different. |
128 |
| - #[cfg(not(any(target_os = "freebsd", target_os = "macos")))] |
129 |
| - assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ERANGE); |
130 |
| - |
131 |
| - #[cfg(target_os = "macos")] |
132 |
| - assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ENAMETOOLONG); |
| 164 | + assert_eq!(cstr.to_bytes(), truncated_name.as_bytes()); |
| 165 | + |
| 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 | + } |
133 | 180 | })
|
134 | 181 | .unwrap()
|
135 | 182 | .join()
|
|
0 commit comments