extern crate libc;
#[derive(Debug, Copy, Clone)]
pub enum Error {
Priority(&'static str),
Pthread(i32),
Ffi(&'static str),
}
pub use libc::sched_param as ScheduleParams;
#[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")),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum ThreadPriority {
Min,
Specific(u8),
Max,
}
impl ThreadPriority {
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 set_thread_priority(
native: libc::pthread_t,
priority: ThreadPriority,
policy: ThreadSchedulePolicy,
) -> Result<(), Error> {
let params = ScheduleParams {
sched_priority: priority.to_posix(policy)?,
};
set_thread_schedule_policy(native, policy, params)
}
pub fn thread_native_id() -> libc::pthread_t {
unsafe { libc::pthread_self() }
}
pub fn thread_schedule_policy() -> Result<ThreadSchedulePolicy, Error> {
unsafe { ThreadSchedulePolicy::from_posix(libc::sched_getscheduler(libc::getpid())) }
}
pub fn set_thread_schedule_policy(
native: libc::pthread_t,
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::Pthread(e)),
}
}
}
pub fn thread_schedule_policy_param(
native: libc::pthread_t,
) -> 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::Pthread(e)),
}
}
}
#[cfg(test)]
mod tests {
use *;
#[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(
thread_id,
ThreadPriority::Min,
ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Normal)
)
.is_ok());
assert!(set_thread_priority(
thread_id,
ThreadPriority::Max,
ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Normal)
)
.is_ok());
assert!(set_thread_priority(
thread_id,
ThreadPriority::Specific(0),
ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Normal)
)
.is_ok());
}
}