Skip to content

Commit

Permalink
Add integration tests and split thread priority getter.
Browse files Browse the repository at this point in the history
1. Adds the integration tests for all supported platforms and common
workflows.
2. Breaks the compatibility with previous versions due to splitting the
   public interface's "thread_priority" function into two separate ones:
   one for getting the value for the current thread and another one for
   getting the value of priority for the thread specified as an
   argument.
  • Loading branch information
iddm committed Jan 22, 2022
1 parent 9ecaeb2 commit 8a2de79
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 38 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "thread-priority"
version = "0.5.0"
version = "0.6.0"
authors = ["Victor Polevoy <[email protected]>"]
description = "Library for managing threads priority and schedule policies"
repository = "https://github.com/vityafx/thread-priority"
Expand All @@ -11,8 +11,12 @@ keywords = ["thread", "schedule", "priority", "pthread"]
categories = ["concurrency", "asynchronous", "os"]
edition = "2018"

[dev-dependencies]
rstest = "0.12"

[dependencies]
log = "0.4"
const_format = "0.2"

[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
libc = "0.2"
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub mod windows;
pub use windows::*;

/// A error type
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Error {
/// A value which describes why it is impossible to use such a priority.
Priority(&'static str),
Expand Down Expand Up @@ -233,7 +233,7 @@ impl Thread {
/// ```
pub fn current() -> Result<Thread, Error> {
Ok(Thread {
priority: thread_priority()?,
priority: get_current_thread_priority()?,
id: thread_native_id(),
})
}
Expand Down
79 changes: 50 additions & 29 deletions src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,27 @@
use std::convert::TryFrom;

use const_format::concatcp;

use crate::{Error, ThreadPriority, ThreadPriorityValue};
use std::mem::MaybeUninit;

#[cfg(not(target_os = "macos"))]
const SCHED_FIFO: i32 = 1;
#[cfg(target_os = "macos")]
const SCHED_FIFO: i32 = 4;

// Processes scheduled under one of the real-time policies
// (SCHED_FIFO, SCHED_RR) have a sched_priority value in the range 1
// (low) to 99 (high).
const MIN_PRIORITY: i32 = 1;
const MAX_PRIORITY: i32 = 99;
// For threads scheduled under one of the normal scheduling policies
// (SCHED_OTHER, SCHED_IDLE, SCHED_BATCH), sched_priority is not
// used in scheduling decisions (it must be specified as 0).
// <https://man7.org/linux/man-pages/man7/sched.7.html>
const NORMAL_PRIORITY: i32 = 0;

/// An alias type for a thread id.
pub type ThreadId = libc::pthread_t;

Expand Down Expand Up @@ -74,13 +92,11 @@ pub enum RealtimeThreadSchedulePolicy {
#[cfg(target_os = "linux")]
Deadline,
}

impl RealtimeThreadSchedulePolicy {
fn to_posix(self) -> libc::c_int {
match self {
#[cfg(not(target_os = "macos"))]
RealtimeThreadSchedulePolicy::Fifo => 1,
#[cfg(target_os = "macos")]
RealtimeThreadSchedulePolicy::Fifo => 4,
RealtimeThreadSchedulePolicy::Fifo => SCHED_FIFO,
RealtimeThreadSchedulePolicy::RoundRobin => 2,
#[cfg(target_os = "linux")]
RealtimeThreadSchedulePolicy::Deadline => 6,
Expand Down Expand Up @@ -147,7 +163,7 @@ impl ThreadSchedulePolicy {
5 => Ok(ThreadSchedulePolicy::Normal(
NormalThreadSchedulePolicy::Idle,
)),
1 => Ok(ThreadSchedulePolicy::Realtime(
SCHED_FIFO => Ok(ThreadSchedulePolicy::Realtime(
RealtimeThreadSchedulePolicy::Fifo,
)),
2 => Ok(ThreadSchedulePolicy::Realtime(
Expand All @@ -167,7 +183,7 @@ impl ThreadSchedulePolicy {
1 => Ok(ThreadSchedulePolicy::Normal(
NormalThreadSchedulePolicy::Other,
)),
4 => Ok(ThreadSchedulePolicy::Realtime(
SCHED_FIFO => Ok(ThreadSchedulePolicy::Realtime(
RealtimeThreadSchedulePolicy::Fifo,
)),
2 => Ok(ThreadSchedulePolicy::Realtime(
Expand All @@ -191,21 +207,23 @@ impl ThreadPriority {
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => Err(
Error::Priority("Deadline scheduling must use deadline priority."),
),
ThreadSchedulePolicy::Realtime(_) => Ok(1),
_ => Ok(0),
ThreadSchedulePolicy::Realtime(_) => Ok(MIN_PRIORITY as u32),
_ => Ok(NORMAL_PRIORITY as u32),
},
ThreadPriority::Crossplatform(ThreadPriorityValue(p)) => match policy {
// SCHED_DEADLINE doesn't really have a notion of priority, this is an error
#[cfg(target_os = "linux")]
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => Err(
Error::Priority("Deadline scheduling must use deadline priority."),
),
ThreadSchedulePolicy::Realtime(_) if (p == 0 || p > 99) => {
Err(Error::Priority("The value is out of range [0; 99]"))
ThreadSchedulePolicy::Realtime(_)
if !((MIN_PRIORITY..=MAX_PRIORITY).contains(&(p as i32))) =>
{
Err(Error::Priority(concatcp!("The value is out of range [", MIN_PRIORITY, "; ", MAX_PRIORITY, "]")))
}
ThreadSchedulePolicy::Normal(_) if p != 0 => Err(Error::Priority(
"The value can be only 0 for normal scheduling policy",
)),
ThreadSchedulePolicy::Normal(_) if p as i32 != NORMAL_PRIORITY => Err(
Error::Priority("The value can be only 0 for normal scheduling policy"),
),
_ => Ok(p as u32),
},
// TODO avoid code duplication.
Expand All @@ -215,12 +233,14 @@ impl ThreadPriority {
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => Err(
Error::Priority("Deadline scheduling must use deadline priority."),
),
ThreadSchedulePolicy::Realtime(_) if (p == 0 || p > 99) => {
Err(Error::Priority("The value is out of range [0; 99]"))
ThreadSchedulePolicy::Realtime(_)
if !((MIN_PRIORITY..=MAX_PRIORITY).contains(&(p as i32))) =>
{
Err(Error::Priority(concatcp!("The value is out of range [", MIN_PRIORITY, "; ", MAX_PRIORITY, "]")))
}
ThreadSchedulePolicy::Normal(_) if p != 0 => Err(Error::Priority(
"The value can be only 0 for normal scheduling policy",
)),
ThreadSchedulePolicy::Normal(_) if p as i32 != NORMAL_PRIORITY => Err(
Error::Priority(concatcp!("The value can be only ", NORMAL_PRIORITY, " for normal scheduling policy")),
),
_ => Ok(p),
},
ThreadPriority::Max => match policy {
Expand All @@ -229,8 +249,8 @@ impl ThreadPriority {
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => Err(
Error::Priority("Deadline scheduling must use deadline priority."),
),
ThreadSchedulePolicy::Realtime(_) => Ok(99),
_ => Ok(0),
ThreadSchedulePolicy::Realtime(_) => Ok(MAX_PRIORITY as u32),
_ => Ok(NORMAL_PRIORITY as u32),
},
#[cfg(target_os = "linux")]
ThreadPriority::Deadline(_, _, _) => Err(Error::Priority(
Expand Down Expand Up @@ -317,11 +337,6 @@ pub fn set_current_thread_priority(priority: ThreadPriority) -> Result<(), Error
/// assert!(thread_schedule_policy().is_ok());
/// ```
pub fn thread_schedule_policy() -> Result<ThreadSchedulePolicy, Error> {
#[cfg(not(target_os = "macos"))]
unsafe {
ThreadSchedulePolicy::from_posix(libc::sched_getscheduler(libc::getpid()))
}
#[cfg(target_os = "macos")]
thread_schedule_policy_param(thread_native_id()).map(|policy| policy.0)
}

Expand Down Expand Up @@ -386,6 +401,7 @@ pub fn set_thread_schedule_policy(
&params as *const libc::sched_param,
),
};
// This is just to silent the unused variable warning.
let _priority = priority;
match ret {
0 => Ok(()),
Expand Down Expand Up @@ -426,13 +442,18 @@ pub fn thread_schedule_policy_param(
}
}

/// Get current thread's priority value.
pub fn thread_priority() -> Result<ThreadPriority, Error> {
/// Get the thread's priority value.
pub fn get_thread_priority(native: ThreadId) -> Result<ThreadPriority, Error> {
Ok(ThreadPriority::from_posix(
thread_schedule_policy_param(thread_native_id())?.1,
thread_schedule_policy_param(native)?.1,
))
}

/// Get current thread's priority value.
pub fn get_current_thread_priority() -> Result<ThreadPriority, Error> {
get_thread_priority(thread_native_id())
}

/// A helper trait for other threads to implement to be able to call methods
/// on threads themselves.
///
Expand All @@ -456,7 +477,7 @@ pub trait ThreadExt {
/// assert!(std::thread::current().get_priority().is_ok());
/// ```
fn get_priority(&self) -> Result<ThreadPriority, Error> {
thread_priority()
get_current_thread_priority()
}

/// Sets the current thread's priority.
Expand Down
36 changes: 30 additions & 6 deletions src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,20 +206,44 @@ pub fn set_current_thread_priority(priority: ThreadPriority) -> Result<(), Error
set_thread_priority(thread_id, priority)
}

/// Get the thread's priority value.
///
/// Returns current thread's priority.
///
/// # Usage
///
/// ```rust
/// use thread_priority::get_thread_priority;
///
/// assert!(get_priority().is_ok());
/// ```
pub fn get_thread_priority(native: ThreadId) -> Result<ThreadPriority, Error> {
use std::convert::TryFrom;

unsafe {
let ret = GetThreadPriority(native);
if ret as u32 != winbase::THREAD_PRIORITY_ERROR_RETURN {
Ok(ThreadPriority::Os(crate::ThreadPriorityOsValue(
WinAPIThreadPriority::try_from(ret as DWORD)? as u32,
)))
} else {
Err(Error::OS(GetLastError() as i32))
}
}
}

/// Get current thread's priority value.
///
/// Returns current thread id, which is the current OS's native handle.
/// It may or may not be equal or even related to rust's thread id,
/// there is absolutely no guarantee for that.
/// Returns current thread's priority.
///
/// # Usage
///
/// ```rust
/// use thread_priority::thread_priority;
/// use thread_priority::get_current_thread_priority;
///
/// assert!(thread_priority().is_ok());
/// assert!(get_thread_priority().is_ok());
/// ```
pub fn thread_priority() -> Result<ThreadPriority, Error> {
pub fn get_current_thread_priority() -> Result<ThreadPriority, Error> {
use std::convert::TryFrom;

unsafe {
Expand Down
11 changes: 11 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
mod unix;
#[cfg(windows)]
mod windows;
Loading

0 comments on commit 8a2de79

Please sign in to comment.