use core::ffi::{c_int, c_void};
unsafe extern "C" {
fn pthread_set_qos_class_self_np(qos_class: u32, relative_priority: c_int) -> c_int;
fn pthread_get_qos_class_np(thread: *mut c_void, qos_class: *mut u32, relative_priority: *mut c_int) -> c_int;
fn pthread_self() -> *mut c_void;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u32)]
pub enum QosClass {
UserInteractive = 0x21,
UserInitiated = 0x19,
Default = 0x15,
Utility = 0x11,
Background = 0x09,
Unspecified = 0x00,
}
impl QosClass {
const fn from_raw(raw: u32) -> Option<Self> {
Some(match raw {
0x21 => Self::UserInteractive,
0x19 => Self::UserInitiated,
0x15 => Self::Default,
0x11 => Self::Utility,
0x09 => Self::Background,
0x00 => Self::Unspecified,
_ => return None,
})
}
}
pub const MIN_RELATIVE_PRIORITY: c_int = -15;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct QosError(c_int);
impl QosError {
#[inline]
pub const fn code(self) -> c_int {
self.0
}
}
impl core::fmt::Display for QosError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "failed to set thread QoS class (error {})", self.0)
}
}
#[cfg(feature = "std")]
impl std::error::Error for QosError {}
#[inline]
pub fn set_thread_qos(class: QosClass, relative_priority: c_int) -> Result<(), QosError> {
const EINVAL: c_int = 22;
if matches!(class, QosClass::Unspecified) || !(MIN_RELATIVE_PRIORITY..=0).contains(&relative_priority) {
return Err(QosError(EINVAL));
}
match unsafe { pthread_set_qos_class_self_np(class as u32, relative_priority) } {
0 => Ok(()),
code => Err(QosError(code)),
}
}
#[inline]
pub fn thread_qos() -> Option<(QosClass, c_int)> {
let mut class = 0u32;
let mut relative_priority = 0 as c_int;
let rc = unsafe { pthread_get_qos_class_np(pthread_self(), &raw mut class, &raw mut relative_priority) };
(rc == 0).then(|| Some((QosClass::from_raw(class)?, relative_priority)))?
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_values_round_trip() {
for class in [
QosClass::UserInteractive,
QosClass::UserInitiated,
QosClass::Default,
QosClass::Utility,
QosClass::Background,
QosClass::Unspecified,
] {
assert_eq!(QosClass::from_raw(class as u32), Some(class));
}
assert_eq!(QosClass::from_raw(0xdead), None);
assert!(QosClass::UserInteractive > QosClass::Background);
}
#[test]
fn rejects_invalid_requests() {
assert!(set_thread_qos(QosClass::Unspecified, 0).is_err());
assert!(set_thread_qos(QosClass::Utility, 1).is_err());
assert!(set_thread_qos(QosClass::Utility, MIN_RELATIVE_PRIORITY - 1).is_err());
}
#[test]
fn set_and_read_back() {
if set_thread_qos(QosClass::Utility, 0).is_ok() {
assert_eq!(thread_qos(), Some((QosClass::Utility, 0)));
}
}
}