Skip to content

Commit 027860e

Browse files
committed
rework threadname test for more consistency
1 parent 00ae882 commit 027860e

File tree

2 files changed

+122
-62
lines changed

2 files changed

+122
-62
lines changed

tests/pass-dep/libc/pthread-threadname.rs

+109-62
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
//@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};
53
use std::thread;
64

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+
721
fn main() {
822
// The short name should be shorter than 16 bytes which POSIX promises
923
// for thread names. The length includes a null terminator.
@@ -52,84 +66,117 @@ fn main() {
5266
}
5367

5468
thread::Builder::new()
55-
.name(short_name.clone())
5669
.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);
5974

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.
6278
let mut buf = vec![0u8; short_name.len().max(15) + 1];
6379
assert_eq!(get_thread_name(&mut buf), 0);
64-
6580
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+
}
7799

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+
}
88115

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+
}
95127
}
96128
})
97129
.unwrap()
98130
.join()
99131
.unwrap();
100132

101133
thread::Builder::new()
102-
.name(long_name.clone())
103134
.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.
106159

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.
110161
let mut buf = vec![0u8; long_name.len() + 1];
111162
assert_eq!(get_thread_name(&mut buf), 0);
112-
113163
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+
}
133180
})
134181
.unwrap()
135182
.join()

tests/pass/concurrency/threadname.rs

+13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ fn main() {
1616
.join()
1717
.unwrap();
1818

19+
// Long thread name.
20+
let long_name = std::iter::once("test_named_thread_truncation")
21+
.chain(std::iter::repeat(" long").take(100))
22+
.collect::<String>();
23+
thread::Builder::new()
24+
.name(long_name.clone())
25+
.spawn(move || {
26+
assert_eq!(thread::current().name().unwrap(), long_name);
27+
})
28+
.unwrap()
29+
.join()
30+
.unwrap();
31+
1932
// Also check main thread name.
2033
assert_eq!(thread::current().name().unwrap(), "main");
2134
}

0 commit comments

Comments
 (0)