Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pthread_setname_np/pthread_getname_np wrappers. #1539

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

devnexen
Copy link
Contributor

linux support only for now.

@devnexen devnexen force-pushed the pthread_ids branch 3 times, most recently from 62e3bd2 to daa7a93 Compare September 25, 2021 14:14
linux gnu support only for now.
/// Obtain the name of the thread
///
/// On linux the name cannot exceed 16 length including null terminator
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetBSD and FreeBSD also support these functions. You should include them, though first you'll have to add FreeBSD's to libc.

Copy link
Contributor Author

@devnexen devnexen Sep 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know but I wanted to see first if it would appeal ;-) I added missing libc calls earlier anyway.

/// On linux the name cannot exceed 16 length including null terminator
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
pub fn pthread_getname_np(thread: Pthread) -> Result<String> {
let mut name = [0u8; 16];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 16 bytes thing is Linux specific. On NetBSD it's controlled by the PTHREAD_MAX_NAMELEN_NP constant, currently 32 bytes. On FreeBSD it's not clearly documented, but the limit is MAXCOMLEN + 1, which is 20.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very true but as mentionned in the commit message I focus on Linux for now.

/// Set the name of the thread
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
pub fn pthread_setname_np(thread: Pthread, name: String) {
let cname = CString::new(name).expect("name failed");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to return an error here instead of panic.


/// Set the name of the thread
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
pub fn pthread_setname_np(thread: Pthread, name: String) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could be a little more generic here and accept any T: Into<Vec<u8>>. That way the caller could provide, for example, a byte string b"nix-rust".

///
/// On linux the name cannot exceed 16 length including null terminator
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
pub fn pthread_getname_np(thread: Pthread) -> Result<String> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggesting returning CString and letting the caller convert it if he wants to.

@devnexen
Copy link
Contributor Author

before moving further (libc had been updated since) what do you think ?

let mut name = [0u8; 16];
pub fn pthread_getname_np(thread: Pthread) -> Result<CString> {
let mut name: Vec<u8> = Vec::with_capacity(16);
unsafe { name.set_len(16); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically UB. You shouldn't call set_len until after the the FFI function returns successfully, and you should only set it to as many bytes as the FFI function actually wrote. In this case, pthread_getname_np doesn't actually return the length of the name, so you'll have to fully initialize the buffer anyway.

unsafe { libc::pthread_getname_np(thread, name.as_mut_ptr() as _, name.len()) };
let cname = unsafe { CStr::from_ptr(name.as_ptr() as _) };
Ok(cname.to_owned().to_string_lossy().to_string())
let zi = name.iter().position(|x| *x == b'\0').unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This three lines can be simplified. If you preinitialize the buffer to zero, then all you'll have to do is CString::new(name).

let res = unsafe { libc::pthread_setname_np(thread, nameptr) };
Errno::result(res).map(drop)
},
Err(_) => Ok(()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't blindly ignore errors. You should return an error in this case.

Ok(unsafe { CString::from_vec_unchecked(name) })
}

/// Set the name of the thread
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A doc test would be good here, especially because it's not obvious what the type of the argument should be.

#[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn test_pthread_name_np() {
let tid = pthread_self();
let name = b"nix-rust";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that it takes three lines to put name into the right format suggests that the function's interface isn't well-designed. The goal is to be able to call it in any convenient way, like this:

pthread_setname_np(tid, "foo");    // Rust string
pthread_setname_np(tid, b"foo");    // Byte vector
pthread_setname_np(tid, CString::new("foo"));    // C String

let tid = pthread_self();
let name = b"nix-rust";
let namestr = std::str::from_utf8(name).unwrap();
pthread_setname_np(tid, name.to_vec()).expect("Should pass");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect should only be used in combination with a useful message. If you're just going to say "Should pass", then you might as well just use unwrap().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants