#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ThreadPriority {
Worker,
LatencySensitive,
}
impl ThreadPriority {
pub fn apply_to_current_thread(self) {
let class = thread_priority_to_qos_class(self);
set_current_thread_qos_class(class);
}
pub fn assert_is_used_on_current_thread(self) {
if IS_QOS_AVAILABLE {
let class = thread_priority_to_qos_class(self);
assert_eq!(get_current_thread_qos_class(), Some(class));
}
}
}
use imp::QoSClass;
const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE;
fn set_current_thread_qos_class(class: QoSClass) {
imp::set_current_thread_qos_class(class);
}
fn get_current_thread_qos_class() -> Option<QoSClass> {
imp::get_current_thread_qos_class()
}
fn thread_priority_to_qos_class(priority: ThreadPriority) -> QoSClass {
imp::thread_priority_to_qos_class(priority)
}
#[cfg(target_vendor = "apple")]
mod imp {
use super::ThreadPriority;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum QoSClass {
Background,
Utility,
UserInitiated,
UserInteractive,
}
pub const IS_QOS_AVAILABLE: bool = true;
pub fn set_current_thread_qos_class(class: QoSClass) {
let c = match class {
QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE,
QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED,
QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY,
QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND,
};
#[allow(unsafe_code)]
let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) };
if code == 0 {
return;
}
#[allow(unsafe_code)]
let errno = unsafe { *libc::__error() };
match errno {
libc::EPERM => {
panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})")
}
libc::EINVAL => {
unreachable!(
"invalid qos_class_t value was passed to pthread_set_qos_class_self_np"
)
}
_ => {
unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}")
}
}
}
pub fn get_current_thread_qos_class() -> Option<QoSClass> {
#[allow(unsafe_code)]
let current_thread = unsafe { libc::pthread_self() };
let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED;
#[allow(unsafe_code)]
let code = unsafe {
libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut())
};
if code != 0 {
#[allow(unsafe_code)]
let errno = unsafe { *libc::__error() };
unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})");
}
match qos_class_raw {
libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive),
libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated),
libc::qos_class_t::QOS_CLASS_DEFAULT => None, libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility),
libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background),
libc::qos_class_t::QOS_CLASS_UNSPECIFIED => {
panic!("tried to get QoS of thread which has opted out of QoS")
}
}
}
pub fn thread_priority_to_qos_class(priority: ThreadPriority) -> QoSClass {
match priority {
ThreadPriority::Worker => QoSClass::Utility,
ThreadPriority::LatencySensitive => QoSClass::UserInitiated,
}
}
}
#[cfg(not(target_vendor = "apple"))]
mod imp {
use super::ThreadPriority;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum QoSClass {
Default,
}
pub const IS_QOS_AVAILABLE: bool = false;
pub fn set_current_thread_qos_class(_: QoSClass) {}
pub fn get_current_thread_qos_class() -> Option<QoSClass> {
None
}
pub fn thread_priority_to_qos_class(_: ThreadPriority) -> QoSClass {
QoSClass::Default
}
}