Skip to content

Commit 0c825d2

Browse files
committed
WIP
1 parent 8903945 commit 0c825d2

File tree

3 files changed

+158
-25
lines changed

3 files changed

+158
-25
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ libc = ">=0.2.123"
2323

2424
[target.'cfg(windows)'.dependencies]
2525
libc = ">=0.2.123"
26-
winapi = { version = "0.3", features = ["errhandlingapi", "processthreadsapi", "winnt", "minwindef", "winbase"] }
26+
winapi = { version = ">=0.3", features = ["errhandlingapi", "processthreadsapi", "winnt", "minwindef", "winbase"] }

src/lib.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ impl ThreadPriorityValue {
172172
pub const MAX: u8 = 100;
173173
/// The minimum value for a thread priority.
174174
pub const MIN: u8 = 0;
175+
/// The range of allowed values.
176+
pub const RANGE: std::ops::RangeInclusive<u8> = Self::MIN..=Self::MAX;
175177
}
176178

177179
impl std::convert::TryFrom<u8> for ThreadPriorityValue {
@@ -213,8 +215,26 @@ to set it to [`WinAPIThreadPriority::Idle`] when it is really needed.
213215
)]
214216
Min,
215217
/// Holds a platform-independent priority value.
216-
/// Usually used when setting a value, for sometimes it is not possible to map
217-
/// the operating system's priority to this value.
218+
#[cfg_attr(
219+
not(target_os = "windows"),
220+
doc = "\
221+
The value is used in two ways when the underlying OS is POSIX-compatible.
222+
# When setting the priority value
223+
224+
- When a scheduling policy other than real-time is used, the value is mapped to the range
225+
of niceness values, typically ranging from -20 to 19 inclusively, so that the [`ThreadPriority::Min`] would mean the
226+
minimum possible value of niceness (19), the [`ThreadPriority::Max`] the maximum value (-20) and the
227+
[`ThreadPriority::Crossplatform`] variant would be mapped from its range of values (from [`ThreadPriorityValue::MIN`]
228+
to [`ThreadPriorityValue::MAX`]) to the range of [`crate::unix::NICENESS_MIN`] till [`crate::unix::NICENESS_MAX`]).
229+
- When a scheduling policy is the one of the real-time ones, the value is then converted to the range of possible values
230+
for a real-time thread of the specified real-time scheduling policy, being an argument [`libc::sched_param::sched_priority`].
231+
232+
# When obtaining the priority value
233+
234+
- When the scheduling policy is not real-time, the niceness-converted value is returned after mapping to the range of possible values for the [`ThreadPriorityValue`] type ([`ThreadPriorityValue::RANGE`]).
235+
- When the scheduling policy is real-time, the [`libc::sched_param::sched_priority`] value is converted to the range of possible values for the [`ThreadPriorityValue`] type ([`ThreadPriorityValue::RANGE`]).
236+
"
237+
)]
218238
Crossplatform(ThreadPriorityValue),
219239
/// Holds an operating system specific value. If it is not possible to obtain the
220240
/// [`ThreadPriority::Crossplatform`] variant of the value, this is returned instead.

src/unix.rs

Lines changed: 135 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,11 @@ use std::mem::MaybeUninit;
2626
// <https://man7.org/linux/man-pages/man7/sched.7.html>
2727

2828
/// An alias type for a thread id.
29-
pub type ThreadId = libc::pthread_t;
30-
31-
/// The maximum value possible for niceness. Threads with this value
32-
/// of niceness have the highest priority possible
33-
pub const NICENESS_MAX: i8 = -20;
34-
/// The minimum value possible for niceness. Threads with this value
35-
/// of niceness have the lowest priority possible.
36-
pub const NICENESS_MIN: i8 = 19;
29+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
30+
pub struct ThreadId {
31+
pub(crate) pthread_id: libc::pthread_t,
32+
pub(crate) pid: libc::pid_t,
33+
}
3734

3835
/// Proxy structure to maintain compatibility between glibc and musl
3936
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
@@ -260,7 +257,7 @@ impl ThreadPriority {
260257
}
261258
ThreadSchedulePolicy::Normal(_) => {
262259
// Niceness can be used, from -20 to 19, where `-20` is the maximum.
263-
Ok(NICENESS_MAX as libc::c_int)
260+
Ok(Niceness::MAX as libc::c_int)
264261
}
265262
_ => {
266263
let max_priority = unsafe { libc::sched_get_priority_max(policy.to_posix()) };
@@ -281,7 +278,7 @@ impl ThreadPriority {
281278
ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Idle) => Ok(0),
282279
ThreadSchedulePolicy::Normal(_) => {
283280
// Niceness can be used, from -20 to 19, where `-20` is the maximum.
284-
Ok(NICENESS_MIN as libc::c_int)
281+
Ok(Niceness::MIN as libc::c_int)
285282
}
286283
_ => {
287284
let min_priority = unsafe { libc::sched_get_priority_min(policy.to_posix()) };
@@ -347,9 +344,9 @@ impl ThreadPriority {
347344
Self::to_allowed_value_for_policy(p as i32, policy).map(|v| v as u32)
348345
}
349346
ThreadSchedulePolicy::Normal(_) => {
350-
let niceness_values = NICENESS_MAX.abs() + NICENESS_MIN.abs();
347+
let niceness_values = Niceness::MAX.abs() + Niceness::MIN.abs();
351348
let ratio = p as f32 / ThreadPriorityValue::MAX as f32;
352-
let niceness = ((niceness_values as f32 * ratio) as i8 + NICENESS_MAX) as i32;
349+
let niceness = ((niceness_values as f32 * ratio) as i8 + Niceness::MAX) as i32;
353350
Self::to_allowed_value_for_policy(niceness, policy).map(|v| v as u32)
354351
}
355352
},
@@ -427,7 +424,7 @@ fn set_thread_priority_and_policy_deadline(
427424
))
428425
}
429426
};
430-
let tid = native as libc::pid_t;
427+
let tid = native.pthread_id as libc::pid_t;
431428
let sched_attr = SchedAttr {
432429
size: std::mem::size_of::<SchedAttr>() as u32,
433430
sched_policy: RealtimeThreadSchedulePolicy::Deadline.to_posix() as u32,
@@ -497,9 +494,11 @@ pub fn set_thread_priority_and_policy(
497494
}
498495
.into_posix();
499496

497+
set_errno(0);
498+
500499
let ret = unsafe {
501500
libc::pthread_setschedparam(
502-
native,
501+
native.pthread_id,
503502
policy.to_posix(),
504503
&params as *const libc::sched_param,
505504
)
@@ -577,7 +576,7 @@ pub fn thread_schedule_policy_param(
577576
let mut params = ScheduleParams { sched_priority: 0 }.into_posix();
578577

579578
let ret = libc::pthread_getschedparam(
580-
native,
579+
native.pthread_id,
581580
&mut policy as *mut libc::c_int,
582581
&mut params as *mut libc::sched_param,
583582
);
@@ -591,11 +590,116 @@ pub fn thread_schedule_policy_param(
591590
}
592591
}
593592

593+
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
594+
struct Niceness(pub libc::c_int);
595+
impl Niceness {
596+
/// The maximum value possible for niceness. Threads with this value
597+
/// of niceness have the highest priority possible
598+
pub const MAX: i8 = -20;
599+
/// The minimum value possible for niceness. Threads with this value
600+
/// of niceness have the lowest priority possible.
601+
pub const MIN: i8 = 19;
602+
/// The range of possible values.
603+
pub const RANGE: std::ops::RangeInclusive<i8> = Self::MAX..=Self::MIN;
604+
// TODO this is commented out since the std::cmp::{min,max} do not have const fn analogs.
605+
// pub const RANGE: std::ops::RangeInclusive<i8> =
606+
// std::cmp::min(Self::MIN, Self::MAX)..=std::cmp::max(Self::MIN, Self::MAX);
607+
}
608+
609+
impl TryFrom<i8> for Niceness {
610+
type Error = Error;
611+
612+
fn try_from(value: i8) -> Result<Self, Self::Error> {
613+
if Self::RANGE.contains(&value) {
614+
return Ok(Self(value as libc::c_int));
615+
}
616+
Err(Error::PriorityNotInRange(
617+
Self::MIN as i32..=Self::MAX as i32,
618+
))
619+
}
620+
}
621+
622+
impl TryFrom<Niceness> for ThreadPriority {
623+
type Error = Error;
624+
625+
fn try_from(value: Niceness) -> Result<Self, Self::Error> {
626+
Ok(ThreadPriority::Crossplatform(
627+
ThreadPriorityValue::try_from(value)?,
628+
))
629+
}
630+
}
631+
632+
impl TryFrom<ThreadPriorityValue> for Niceness {
633+
type Error = Error;
634+
635+
fn try_from(value: ThreadPriorityValue) -> Result<Self, Self::Error> {
636+
// we need -20;19 from 0-100.
637+
638+
let niceness_values = Niceness::MAX.abs() + Niceness::MIN.abs();
639+
let niceness = (value.0 as f32) / 100.0f32 * (niceness_values as f32);
640+
let niceness = niceness as libc::c_int - (Niceness::MAX.abs() as i32);
641+
642+
Ok(Niceness(niceness))
643+
}
644+
}
645+
646+
impl TryFrom<Niceness> for ThreadPriorityValue {
647+
type Error = Error;
648+
649+
fn try_from(value: Niceness) -> Result<Self, Self::Error> {
650+
// we need 0-100 from 0-40.
651+
let value = value.0 + (Niceness::MAX.abs() as i32);
652+
let niceness_values = Niceness::MAX.abs() + Niceness::MIN.abs();
653+
let priority = ((niceness_values as f32) / 100.0f32 * (value as f32)) as libc::c_int;
654+
655+
if let Ok(priority) = u8::try_from(priority) {
656+
ThreadPriorityValue::try_from(priority).map_err(Error::Priority)
657+
} else {
658+
Err(Error::PriorityNotInRange(
659+
Niceness::MAX as i32..=Niceness::MIN as i32,
660+
))
661+
}
662+
}
663+
}
664+
665+
/// Returns the thread's niceness.
666+
///
667+
/// # Example
668+
/// ```rust
669+
/// use thread_priority::*;
670+
/// use std::convert::TryInto;
671+
///
672+
/// let thread_id = std::thread::current().get_native_id();
673+
/// let niceness = thread_niceness(thread_id);
674+
/// assert_eq!(niceness, Ok(ThreadPriority::Crossplatform(0u8.try_into().unwrap())));
675+
/// ```
676+
pub fn thread_niceness(native: ThreadId) -> Result<ThreadPriority, Error> {
677+
set_errno(0);
678+
679+
dbg!(native);
680+
let thread_pid = u32::try_from(native.pid)
681+
.map_err(|_| Error::Ffi("Couldn't convert the native thread id."))?;
682+
unsafe {
683+
let niceness = match libc::getpriority(libc::PRIO_PROCESS, thread_pid) {
684+
-1 => match errno() {
685+
0 => Niceness::try_from(-1)?,
686+
e => return Err(Error::OS(e)),
687+
},
688+
niceness => Niceness(niceness),
689+
};
690+
let priority = ThreadPriorityValue::try_from(niceness)?;
691+
Ok(ThreadPriority::Crossplatform(priority))
692+
}
693+
}
694+
594695
/// Get the thread's priority value.
595696
pub fn get_thread_priority(native: ThreadId) -> Result<ThreadPriority, Error> {
596-
Ok(ThreadPriority::from_posix(
597-
thread_schedule_policy_param(native)?.1,
598-
))
697+
let posix = thread_schedule_policy_param(native)?;
698+
699+
match posix.0 {
700+
ThreadSchedulePolicy::Normal(_) => thread_niceness(native),
701+
_ => Ok(ThreadPriority::from_posix(posix.1)),
702+
}
599703
}
600704

601705
/// Get current thread's priority value.
@@ -697,19 +801,25 @@ impl ThreadExt for std::thread::Thread {}
697801
/// ```rust
698802
/// use thread_priority::thread_native_id;
699803
///
700-
/// assert!(thread_native_id() > 0);
804+
/// let _thread_id = thread_native_id();
701805
/// ```
702806
pub fn thread_native_id() -> ThreadId {
703-
unsafe { libc::pthread_self() }
807+
let pthread_id = unsafe { libc::pthread_self() };
808+
#[cfg(linux)]
809+
let pid = unsafe { libc::gettid() };
810+
#[cfg(not(linux))]
811+
let pid = unsafe { libc::getpid() };
812+
ThreadId { pthread_id, pid }
704813
}
705814

706815
impl TryFrom<u8> for ThreadPriority {
707816
type Error = &'static str;
708817

709818
fn try_from(value: u8) -> Result<Self, Self::Error> {
710-
if let 0..=100 = value {
819+
if ThreadPriorityValue::RANGE.contains(&value) {
711820
Ok(ThreadPriority::Crossplatform(ThreadPriorityValue(value)))
712821
} else {
822+
// TODO create a string with the range of values as constexpr.
713823
Err("The thread priority value must be in range of [0; 100].")
714824
}
715825
}
@@ -734,7 +844,10 @@ mod tests {
734844
use std::time::Duration;
735845

736846
assert!(set_thread_priority_and_policy(
737-
0, // current thread
847+
ThreadId {
848+
pthread_id: 0,
849+
pid: 0,
850+
},
738851
ThreadPriority::Deadline {
739852
runtime: Duration::from_millis(1),
740853
deadline: Duration::from_millis(10),

0 commit comments

Comments
 (0)