use crate::{
AppliedPriority, Error, Grant, Mechanism, MechanismPolicy, QosClass, Result, ThreadPriority,
platform::macos::scheduling_policy::SchedulingPolicy,
};
fn qos_class_t_of(c: QosClass) -> libc::qos_class_t {
match c {
QosClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND,
QosClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY,
QosClass::Default => libc::qos_class_t::QOS_CLASS_DEFAULT,
QosClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED,
QosClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE,
}
}
fn band_i8(v: libc::c_int) -> i8 {
v.clamp(i8::MIN as libc::c_int, i8::MAX as libc::c_int) as i8
}
pub(crate) fn promote_thread_to_realtime() -> Result<AppliedPriority> {
match SchedulingPolicy::default_for(ThreadPriority::TimeCritical) {
SchedulingPolicy::Absolute { priority } => {
set_sched_rr(ThreadPriority::TimeCritical, priority)
}
SchedulingPolicy::QoS { .. } => Err(Error::Unsupported(
"TimeCritical does not map to SCHED_RR on this macOS target".to_string(),
)),
}
}
fn set_sched_rr(requested: ThreadPriority, priority: libc::c_int) -> Result<AppliedPriority> {
let current_thread = unsafe { libc::pthread_self() };
let (min, max) = unsafe {
(
libc::sched_get_priority_min(libc::SCHED_RR),
libc::sched_get_priority_max(libc::SCHED_RR),
)
};
let priority = priority.clamp(min, max);
let mut param: libc::sched_param = unsafe { std::mem::zeroed() };
param.sched_priority = priority;
let err = unsafe { libc::pthread_setschedparam(current_thread, libc::SCHED_RR, ¶m) };
if err != 0 {
if err == libc::EPERM {
return Err(Error::PermissionDenied(format!(
"SCHED_RR priority {} denied",
priority
)));
}
return Err(Error::Affinity(format!(
"pthread_setschedparam failed with error code: {}",
std::io::Error::from_raw_os_error(err)
)));
}
Ok(AppliedPriority::new(
requested,
requested,
Grant::Realtime,
Mechanism {
policy: MechanismPolicy::SchedRr,
value: band_i8(priority),
},
))
}
pub fn set_thread_priority(priority: ThreadPriority) -> Result<AppliedPriority> {
let requested = priority;
let sched_policy = SchedulingPolicy::default_for(priority);
match sched_policy {
SchedulingPolicy::QoS {
class,
relative_priority,
} => {
let qos_class = qos_class_t_of(class);
let qos_class_str = match class {
QosClass::UserInteractive => "User Interactive",
QosClass::UserInitiated => "User Initiated",
QosClass::Utility => "Utility",
QosClass::Background => "Background",
QosClass::Default => "Default",
};
let err = unsafe { libc::pthread_set_qos_class_self_np(qos_class, relative_priority) };
if err != 0 {
if err == libc::EPERM {
let (min, max) = unsafe {
(
libc::sched_get_priority_min(libc::SCHED_OTHER),
libc::sched_get_priority_max(libc::SCHED_OTHER),
)
};
let mut param: libc::sched_param = unsafe { std::mem::zeroed() };
param.sched_priority = min + (max - min) * (priority as libc::c_int) / 6;
let fb = unsafe {
libc::pthread_setschedparam(libc::pthread_self(), libc::SCHED_OTHER, ¶m)
};
if fb == 0 {
return Ok(AppliedPriority::new(
priority,
priority,
Grant::Direct,
Mechanism {
policy: MechanismPolicy::SchedOther,
value: band_i8(param.sched_priority),
},
));
}
return Err(Error::PermissionDenied(format!(
"QoS class {} denied and SCHED_OTHER fallback failed (error {})",
qos_class_str, fb
)));
}
Err(Error::Affinity(format!(
"pthread_set_qos_class_self_np failed with error code: {}",
std::io::Error::from_raw_os_error(err)
)))
} else {
Ok(AppliedPriority::new(
priority,
priority,
Grant::Direct,
Mechanism {
policy: MechanismPolicy::Qos,
value: class as i8,
},
))
}
}
SchedulingPolicy::Absolute { priority } => {
let current_thread = unsafe { libc::pthread_self() };
let (min, max) = unsafe {
(
libc::sched_get_priority_min(libc::SCHED_RR),
libc::sched_get_priority_max(libc::SCHED_RR),
)
};
let priority = priority.clamp(min, max);
let mut param: libc::sched_param = unsafe { std::mem::zeroed() };
param.sched_priority = priority;
let err =
unsafe { libc::pthread_setschedparam(current_thread, libc::SCHED_RR, ¶m) };
if err != 0 {
if err == libc::EPERM {
let fb = unsafe {
libc::pthread_set_qos_class_self_np(
libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE,
0,
)
};
if fb == 0 {
return Ok(AppliedPriority::new(
requested,
requested,
Grant::Direct,
Mechanism {
policy: MechanismPolicy::Qos,
value: QosClass::UserInteractive as i8,
},
));
}
return Err(Error::PermissionDenied(format!(
"SCHED_RR priority {} denied and QoS fallback failed",
priority
)));
}
Err(Error::Affinity(format!(
"pthread_setschedparam failed with error code: {}",
std::io::Error::from_raw_os_error(err)
)))
} else {
Ok(AppliedPriority::new(
requested,
requested,
Grant::Realtime,
Mechanism {
policy: MechanismPolicy::SchedRr,
value: band_i8(priority),
},
))
}
}
}
}