use crate::{Error, ThreadPriority};
pub use libc::sched_param as ScheduleParams;
pub type ThreadId = libc::pthread_t;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum RealtimeThreadSchedulePolicy {
Fifo,
RoundRobin,
}
impl RealtimeThreadSchedulePolicy {
fn to_posix(self) -> libc::c_int {
match self {
RealtimeThreadSchedulePolicy::Fifo => 1,
RealtimeThreadSchedulePolicy::RoundRobin => 2,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum NormalThreadSchedulePolicy {
Idle,
Batch,
Other,
Normal,
}
impl NormalThreadSchedulePolicy {
fn to_posix(self) -> libc::c_int {
match self {
NormalThreadSchedulePolicy::Idle => 5,
NormalThreadSchedulePolicy::Batch => 3,
NormalThreadSchedulePolicy::Other | NormalThreadSchedulePolicy::Normal => 0,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum ThreadSchedulePolicy {
Normal(NormalThreadSchedulePolicy),
Realtime(RealtimeThreadSchedulePolicy),
}
impl ThreadSchedulePolicy {
fn to_posix(self) -> libc::c_int {
match self {
ThreadSchedulePolicy::Normal(p) => p.to_posix(),
ThreadSchedulePolicy::Realtime(p) => p.to_posix(),
}
}
fn from_posix(policy: libc::c_int) -> Result<ThreadSchedulePolicy, Error> {
match policy {
0 => Ok(ThreadSchedulePolicy::Normal(
NormalThreadSchedulePolicy::Normal,
)),
3 => Ok(ThreadSchedulePolicy::Normal(
NormalThreadSchedulePolicy::Batch,
)),
5 => Ok(ThreadSchedulePolicy::Normal(
NormalThreadSchedulePolicy::Idle,
)),
1 => Ok(ThreadSchedulePolicy::Realtime(
RealtimeThreadSchedulePolicy::Fifo,
)),
2 => Ok(ThreadSchedulePolicy::Realtime(
RealtimeThreadSchedulePolicy::RoundRobin,
)),
_ => Err(Error::Ffi("Can't parse schedule policy from posix")),
}
}
}
impl ThreadPriority {
pub fn to_posix(self, policy: ThreadSchedulePolicy) -> Result<libc::c_int, Error> {
let ret = match self {
ThreadPriority::Min => match policy {
ThreadSchedulePolicy::Realtime(_) => Ok(1),
_ => Ok(0),
},
ThreadPriority::Specific(p) => match policy {
ThreadSchedulePolicy::Realtime(_) if (p == 0 || p > 99) => {
Err(Error::Priority("The value is out of range [0; 99]"))
}
ThreadSchedulePolicy::Normal(_) if p != 0 => Err(Error::Priority(
"The value can be only 0 for normal scheduling policy",
)),
_ => Ok(p),
},
ThreadPriority::Max => match policy {
ThreadSchedulePolicy::Realtime(_) => Ok(99),
_ => Ok(0),
},
};
ret.map(|p| p as libc::c_int)
}
pub fn from_posix(params: ScheduleParams) -> ThreadPriority {
ThreadPriority::Specific(params.sched_priority as u32)
}
}
pub fn set_thread_priority_and_policy(
native: ThreadId,
priority: ThreadPriority,
policy: ThreadSchedulePolicy,
) -> Result<(), Error> {
let params = ScheduleParams {
sched_priority: priority.to_posix(policy)?,
};
set_thread_schedule_policy(native, policy, params)
}
pub fn set_current_thread_priority(priority: ThreadPriority) -> Result<(), Error> {
let thread_id = thread_native_id();
let policy = ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Normal);
set_thread_priority_and_policy(thread_id, priority, policy)
}
pub fn thread_schedule_policy() -> Result<ThreadSchedulePolicy, Error> {
unsafe { ThreadSchedulePolicy::from_posix(libc::sched_getscheduler(libc::getpid())) }
}
pub fn set_thread_schedule_policy(
native: ThreadId,
policy: ThreadSchedulePolicy,
params: ScheduleParams,
) -> Result<(), Error> {
unsafe {
let ret = libc::pthread_setschedparam(
native,
policy.to_posix(),
¶ms as *const ScheduleParams,
);
match ret {
0 => Ok(()),
e => Err(Error::OS(e)),
}
}
}
pub fn thread_schedule_policy_param(
native: ThreadId,
) -> Result<(ThreadSchedulePolicy, ScheduleParams), Error> {
unsafe {
let mut policy = 0 as libc::c_int;
let mut params = ScheduleParams { sched_priority: 0 };
let ret = libc::pthread_getschedparam(
native,
&mut policy as *mut libc::c_int,
&mut params as *mut ScheduleParams,
);
match ret {
0 => Ok((ThreadSchedulePolicy::from_posix(policy)?, params)),
e => Err(Error::OS(e)),
}
}
}
pub fn thread_priority() -> Result<ThreadPriority, Error> {
Ok(ThreadPriority::from_posix(
thread_schedule_policy_param(thread_native_id())?.1,
))
}
pub fn thread_native_id() -> ThreadId {
unsafe { libc::pthread_self() }
}
#[cfg(test)]
mod tests {
use crate::unix::*;
#[test]
fn thread_schedule_policy_param_test() {
let thread_id = thread_native_id();
assert!(thread_schedule_policy_param(thread_id).is_ok());
}
#[test]
fn set_thread_priority_test() {
let thread_id = thread_native_id();
assert!(set_thread_priority_and_policy(
thread_id,
ThreadPriority::Min,
ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Normal)
)
.is_ok());
assert!(set_thread_priority_and_policy(
thread_id,
ThreadPriority::Max,
ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Normal)
)
.is_ok());
assert!(set_thread_priority_and_policy(
thread_id,
ThreadPriority::Specific(0),
ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Normal)
)
.is_ok());
}
}