|
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 | +constMAX_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 | fnmain(){ |
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 | letmut 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 | letmut 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