use windows::Win32::{
Foundation::{GetLastError, HANDLE},
System::{
Threading::{
GetCurrentThread, GetThreadPriority, SetThreadIdealProcessor, SetThreadPriority,
SetThreadPriorityBoost, THREAD_MODE_BACKGROUND_BEGIN, THREAD_MODE_BACKGROUND_END,
THREAD_PRIORITY, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_TIME_CRITICAL,
},
WindowsProgramming::THREAD_PRIORITY_ERROR_RETURN,
},
};
use crate::{Error, ThreadPriority};
pub type IdealProcessor = u32;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct ThreadId(HANDLE);
impl PartialOrd for ThreadId {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ThreadId {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.0.0 as usize).cmp(&(other.0.0 as usize))
}
}
impl ThreadId {
pub fn is_invalid(&self) -> bool {
self.0.is_invalid()
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum WinAPIThreadPriority {
BackgroundModeBegin = THREAD_MODE_BACKGROUND_BEGIN.0,
BackgroundModeEnd = THREAD_MODE_BACKGROUND_END.0,
AboveNormal = THREAD_PRIORITY_ABOVE_NORMAL.0,
BelowNormal = THREAD_PRIORITY_BELOW_NORMAL.0,
Highest = THREAD_PRIORITY_HIGHEST.0,
Idle = THREAD_PRIORITY_IDLE.0,
Lowest = THREAD_PRIORITY_LOWEST.0,
Normal = THREAD_PRIORITY_NORMAL.0,
TimeCritical = THREAD_PRIORITY_TIME_CRITICAL.0,
}
impl std::convert::TryFrom<ThreadPriority> for WinAPIThreadPriority {
type Error = crate::Error;
fn try_from(priority: ThreadPriority) -> Result<Self, Self::Error> {
Ok(match priority {
ThreadPriority::Min => WinAPIThreadPriority::Lowest,
ThreadPriority::Crossplatform(crate::ThreadPriorityValue(p)) => match p {
0 => WinAPIThreadPriority::Idle,
1..=19 => WinAPIThreadPriority::Lowest,
20..=39 => WinAPIThreadPriority::BelowNormal,
40..=59 => WinAPIThreadPriority::Normal,
60..=79 => WinAPIThreadPriority::AboveNormal,
80..=98 => WinAPIThreadPriority::Highest,
99 => WinAPIThreadPriority::TimeCritical,
_ => return Err(Error::Priority("The value is out of range [0; 99].")),
},
ThreadPriority::Os(crate::ThreadPriorityOsValue(p)) => {
match THREAD_PRIORITY(p as i32) {
THREAD_MODE_BACKGROUND_BEGIN => WinAPIThreadPriority::BackgroundModeBegin,
THREAD_MODE_BACKGROUND_END => WinAPIThreadPriority::BackgroundModeEnd,
THREAD_PRIORITY_ABOVE_NORMAL => WinAPIThreadPriority::AboveNormal,
THREAD_PRIORITY_BELOW_NORMAL => WinAPIThreadPriority::BelowNormal,
THREAD_PRIORITY_HIGHEST => WinAPIThreadPriority::Highest,
THREAD_PRIORITY_IDLE => WinAPIThreadPriority::Idle,
THREAD_PRIORITY_LOWEST => WinAPIThreadPriority::Lowest,
THREAD_PRIORITY_NORMAL => WinAPIThreadPriority::Normal,
THREAD_PRIORITY_TIME_CRITICAL => WinAPIThreadPriority::TimeCritical,
_ => {
return Err(Error::Priority(
"The value is out of range of allowed values.",
));
}
}
}
ThreadPriority::Max => WinAPIThreadPriority::Highest,
})
}
}
impl std::convert::TryFrom<THREAD_PRIORITY> for WinAPIThreadPriority {
type Error = crate::Error;
fn try_from(priority: THREAD_PRIORITY) -> Result<Self, Self::Error> {
Ok(match priority {
THREAD_MODE_BACKGROUND_BEGIN => WinAPIThreadPriority::BackgroundModeBegin,
THREAD_MODE_BACKGROUND_END => WinAPIThreadPriority::BackgroundModeEnd,
THREAD_PRIORITY_ABOVE_NORMAL => WinAPIThreadPriority::AboveNormal,
THREAD_PRIORITY_BELOW_NORMAL => WinAPIThreadPriority::BelowNormal,
THREAD_PRIORITY_HIGHEST => WinAPIThreadPriority::Highest,
THREAD_PRIORITY_IDLE => WinAPIThreadPriority::Idle,
THREAD_PRIORITY_LOWEST => WinAPIThreadPriority::Lowest,
THREAD_PRIORITY_NORMAL => WinAPIThreadPriority::Normal,
THREAD_PRIORITY_TIME_CRITICAL => WinAPIThreadPriority::TimeCritical,
_ => return Err(Error::Priority("Priority couldn't be parsed")),
})
}
}
impl From<WinAPIThreadPriority> for crate::ThreadPriorityOsValue {
fn from(p: WinAPIThreadPriority) -> Self {
crate::ThreadPriorityOsValue(p as u32)
}
}
pub fn set_thread_priority(native: ThreadId, priority: ThreadPriority) -> Result<(), Error> {
set_winapi_thread_priority(native, WinAPIThreadPriority::try_from(priority)?)
}
pub fn set_winapi_thread_priority(
native: ThreadId,
priority: WinAPIThreadPriority,
) -> Result<(), Error> {
unsafe {
SetThreadPriority(native.0, THREAD_PRIORITY(priority as i32))
.map_err(|e| Error::OS(e.code().0))
}
}
pub fn set_current_thread_priority(priority: ThreadPriority) -> Result<(), Error> {
let thread_id = thread_native_id();
set_thread_priority(thread_id, priority)
}
pub fn get_thread_priority(native: ThreadId) -> Result<ThreadPriority, Error> {
unsafe {
let ret = GetThreadPriority(native.0);
if ret as u32 != THREAD_PRIORITY_ERROR_RETURN {
Ok(ThreadPriority::Os(crate::ThreadPriorityOsValue::from(
WinAPIThreadPriority::try_from(THREAD_PRIORITY(ret))?,
)))
} else {
Err(Error::OS(GetLastError().0 as i32))
}
}
}
pub fn get_current_thread_priority() -> Result<ThreadPriority, Error> {
unsafe {
let ret = GetThreadPriority(thread_native_id().0);
if ret as u32 != THREAD_PRIORITY_ERROR_RETURN {
Ok(ThreadPriority::Os(crate::ThreadPriorityOsValue::from(
WinAPIThreadPriority::try_from(THREAD_PRIORITY(ret))?,
)))
} else {
Err(Error::OS(GetLastError().0 as i32))
}
}
}
pub fn thread_native_id() -> ThreadId {
ThreadId(unsafe { GetCurrentThread() })
}
pub fn set_thread_priority_boost(native: ThreadId, enabled: bool) -> Result<(), Error> {
unsafe { SetThreadPriorityBoost(native.0, enabled).map_err(|e| Error::OS(e.code().0)) }
}
pub fn set_current_thread_priority_boost(enabled: bool) -> Result<(), Error> {
set_thread_priority_boost(thread_native_id(), enabled)
}
pub fn set_thread_ideal_processor(
native: ThreadId,
ideal_processor: IdealProcessor,
) -> Result<IdealProcessor, Error> {
unsafe {
let ret = SetThreadIdealProcessor(native.0, ideal_processor);
if ret == u32::MAX {
Err(Error::OS(GetLastError().0 as i32))
} else {
Ok(ret)
}
}
}
pub fn set_current_thread_ideal_processor(
ideal_processor: IdealProcessor,
) -> Result<IdealProcessor, Error> {
set_thread_ideal_processor(thread_native_id(), ideal_processor)
}
impl std::convert::TryFrom<u32> for crate::ThreadPriorityOsValue {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(crate::ThreadPriorityOsValue(
match THREAD_PRIORITY(value as i32) {
THREAD_MODE_BACKGROUND_BEGIN
| THREAD_MODE_BACKGROUND_END
| THREAD_PRIORITY_ABOVE_NORMAL
| THREAD_PRIORITY_BELOW_NORMAL
| THREAD_PRIORITY_HIGHEST
| THREAD_PRIORITY_IDLE
| THREAD_PRIORITY_LOWEST
| THREAD_PRIORITY_NORMAL
| THREAD_PRIORITY_TIME_CRITICAL => value,
_ => return Err(()),
},
))
}
}
pub trait ThreadExt {
fn get_priority(&self) -> Result<ThreadPriority, Error> {
get_current_thread_priority()
}
fn set_priority(&self, priority: ThreadPriority) -> Result<(), Error> {
set_current_thread_priority(priority)
}
fn get_native_id(&self) -> Result<ThreadId, Error>;
fn set_ideal_processor(
&self,
ideal_processor: IdealProcessor,
) -> Result<IdealProcessor, Error> {
set_current_thread_ideal_processor(ideal_processor)
}
fn set_priority_boost(&self, enabled: bool) -> Result<(), Error> {
set_current_thread_priority_boost(enabled)
}
}
impl ThreadExt for std::thread::Thread {
fn get_native_id(&self) -> Result<ThreadId, Error> {
if self.id() == std::thread::current().id() {
Ok(thread_native_id())
} else {
Err(Error::Priority(
"The `ThreadExt::get_native_id()` is currently limited to be called on the current thread.",
))
}
}
}