use winapi::ctypes::c_int;
use winapi::shared::minwindef::DWORD;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::processthreadsapi::{
GetCurrentThread, GetThreadPriority, SetThreadIdealProcessor, SetThreadPriority,
SetThreadPriorityBoost,
};
use winapi::um::winbase;
use winapi::um::winnt::HANDLE;
use crate::{Error, ThreadPriority};
pub type IdealProcessor = DWORD;
pub type ThreadId = HANDLE;
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum WinAPIThreadPriority {
BackgroundModeBegin = winbase::THREAD_MODE_BACKGROUND_BEGIN,
BackgroundModeEnd = winbase::THREAD_MODE_BACKGROUND_END,
AboveNormal = winbase::THREAD_PRIORITY_ABOVE_NORMAL,
BelowNormal = winbase::THREAD_PRIORITY_BELOW_NORMAL,
Highest = winbase::THREAD_PRIORITY_HIGHEST,
Idle = winbase::THREAD_PRIORITY_IDLE,
Lowest = winbase::THREAD_PRIORITY_LOWEST,
Normal = winbase::THREAD_PRIORITY_NORMAL,
TimeCritical = winbase::THREAD_PRIORITY_TIME_CRITICAL,
}
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,
21..=39 => WinAPIThreadPriority::BelowNormal,
41..=59 => WinAPIThreadPriority::Normal,
61..=79 => WinAPIThreadPriority::AboveNormal,
81..=98 => WinAPIThreadPriority::Highest,
99 => WinAPIThreadPriority::TimeCritical,
_ => return Err(Error::Priority("The value is out of range [0; 99].")),
},
ThreadPriority::Os(crate::ThreadPriorityOsValue(p)) => match p {
winbase::THREAD_MODE_BACKGROUND_BEGIN => WinAPIThreadPriority::BackgroundModeBegin,
winbase::THREAD_MODE_BACKGROUND_END => WinAPIThreadPriority::BackgroundModeEnd,
winbase::THREAD_PRIORITY_ABOVE_NORMAL => WinAPIThreadPriority::AboveNormal,
winbase::THREAD_PRIORITY_BELOW_NORMAL => WinAPIThreadPriority::BelowNormal,
winbase::THREAD_PRIORITY_HIGHEST => WinAPIThreadPriority::Highest,
winbase::THREAD_PRIORITY_IDLE => WinAPIThreadPriority::Idle,
winbase::THREAD_PRIORITY_LOWEST => WinAPIThreadPriority::Lowest,
winbase::THREAD_PRIORITY_NORMAL => WinAPIThreadPriority::Normal,
winbase::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<DWORD> for WinAPIThreadPriority {
type Error = crate::Error;
fn try_from(priority: DWORD) -> Result<Self, Self::Error> {
Ok(match priority {
winbase::THREAD_MODE_BACKGROUND_BEGIN => WinAPIThreadPriority::BackgroundModeBegin,
winbase::THREAD_MODE_BACKGROUND_END => WinAPIThreadPriority::BackgroundModeEnd,
winbase::THREAD_PRIORITY_ABOVE_NORMAL => WinAPIThreadPriority::AboveNormal,
winbase::THREAD_PRIORITY_BELOW_NORMAL => WinAPIThreadPriority::BelowNormal,
winbase::THREAD_PRIORITY_HIGHEST => WinAPIThreadPriority::Highest,
winbase::THREAD_PRIORITY_IDLE => WinAPIThreadPriority::Idle,
winbase::THREAD_PRIORITY_LOWEST => WinAPIThreadPriority::Lowest,
winbase::THREAD_PRIORITY_NORMAL => WinAPIThreadPriority::Normal,
winbase::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 {
if SetThreadPriority(native, priority as c_int) != 0 {
Ok(())
} else {
Err(Error::OS(GetLastError() as i32))
}
}
}
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);
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))
}
}
}
pub fn get_current_thread_priority() -> Result<ThreadPriority, Error> {
unsafe {
let ret = GetThreadPriority(thread_native_id());
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))
}
}
}
pub fn thread_native_id() -> ThreadId {
unsafe { GetCurrentThread() }
}
pub fn set_thread_priority_boost(native: ThreadId, enabled: bool) -> Result<(), Error> {
unsafe {
if SetThreadPriorityBoost(native, enabled as i32) != 0 {
Ok(())
} else {
Err(Error::OS(GetLastError() as i32))
}
}
}
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, ideal_processor);
if ret == IdealProcessor::max_value() - 1 {
Err(Error::OS(GetLastError() 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 value {
winbase::THREAD_MODE_BACKGROUND_BEGIN
| winbase::THREAD_MODE_BACKGROUND_END
| winbase::THREAD_PRIORITY_ABOVE_NORMAL
| winbase::THREAD_PRIORITY_BELOW_NORMAL
| winbase::THREAD_PRIORITY_HIGHEST
| winbase::THREAD_PRIORITY_IDLE
| winbase::THREAD_PRIORITY_LOWEST
| winbase::THREAD_PRIORITY_NORMAL
| winbase::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."))
}
}
}