thread_priority/
windows.rs

1//! This module defines the windows thread control.
2//!
3//! The crate's prelude doesn't have much control over
4//! the windows threads, and this module provides
5//! better control over those.
6
7use winapi::ctypes::c_int;
8use winapi::shared::minwindef::DWORD;
9use winapi::um::errhandlingapi::GetLastError;
10use winapi::um::processthreadsapi::{
11    GetCurrentThread, GetThreadPriority, SetThreadIdealProcessor, SetThreadPriority,
12    SetThreadPriorityBoost,
13};
14use winapi::um::winbase;
15use winapi::um::winnt::HANDLE;
16
17use crate::{Error, ThreadPriority};
18
19/// An alias type for specifying the ideal processor.
20/// Used in the WinAPI for affinity control.
21pub type IdealProcessor = DWORD;
22
23/// An alias type for a thread id.
24pub type ThreadId = HANDLE;
25
26/// The WinAPI priority representation. Check out MSDN for more info:
27/// <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadpriority>
28#[repr(u32)]
29#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
30pub enum WinAPIThreadPriority {
31    /// Begin background processing mode. The system lowers the resource
32    /// scheduling priorities of the thread so that it can perform background
33    /// work without significantly affecting activity in the foreground.
34    ///
35    /// This value can be specified only if hThread is a handle to the current
36    /// thread. The function fails if the thread is already in background processing mode.
37    ///
38    /// # Warning
39    /// Windows Server 2003: This value is not supported.
40    BackgroundModeBegin = winbase::THREAD_MODE_BACKGROUND_BEGIN,
41    /// End background processing mode. The system restores the resource
42    /// scheduling priorities of the thread as they were before the thread
43    /// entered background processing mode.
44    ///
45    /// This value can be specified only if hThread is a handle to the current thread.
46    /// The function fails if the thread is not in background processing mode.
47    ///
48    /// # Warning
49    /// Windows Server 2003: This value is not supported.
50    BackgroundModeEnd = winbase::THREAD_MODE_BACKGROUND_END,
51    /// Priority 1 point above the priority class.
52    AboveNormal = winbase::THREAD_PRIORITY_ABOVE_NORMAL,
53    /// Priority 1 point below the priority class.
54    BelowNormal = winbase::THREAD_PRIORITY_BELOW_NORMAL,
55    /// Priority 2 points above the priority class.
56    Highest = winbase::THREAD_PRIORITY_HIGHEST,
57    /// Base priority of 1 for IDLE_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS,
58    /// NORMAL_PRIORITY_CLASS, ABOVE_NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS
59    /// processes, and a base priority of 16 for REALTIME_PRIORITY_CLASS processes.
60    Idle = winbase::THREAD_PRIORITY_IDLE,
61    /// Priority 2 points below the priority class.
62    Lowest = winbase::THREAD_PRIORITY_LOWEST,
63    /// Normal priority for the priority class.
64    Normal = winbase::THREAD_PRIORITY_NORMAL,
65    /// Base priority of 15 for IDLE_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS,
66    /// NORMAL_PRIORITY_CLASS, ABOVE_NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS
67    /// processes, and a base priority of 31 for REALTIME_PRIORITY_CLASS processes.
68    TimeCritical = winbase::THREAD_PRIORITY_TIME_CRITICAL,
69}
70
71impl std::convert::TryFrom<ThreadPriority> for WinAPIThreadPriority {
72    type Error = crate::Error;
73
74    fn try_from(priority: ThreadPriority) -> Result<Self, Self::Error> {
75        Ok(match priority {
76            ThreadPriority::Min => WinAPIThreadPriority::Lowest,
77            ThreadPriority::Crossplatform(crate::ThreadPriorityValue(p)) => match p {
78                0 => WinAPIThreadPriority::Idle,
79                1..=19 => WinAPIThreadPriority::Lowest,
80                21..=39 => WinAPIThreadPriority::BelowNormal,
81                41..=59 => WinAPIThreadPriority::Normal,
82                61..=79 => WinAPIThreadPriority::AboveNormal,
83                81..=98 => WinAPIThreadPriority::Highest,
84                99 => WinAPIThreadPriority::TimeCritical,
85                _ => return Err(Error::Priority("The value is out of range [0; 99].")),
86            },
87            ThreadPriority::Os(crate::ThreadPriorityOsValue(p)) => match p {
88                winbase::THREAD_MODE_BACKGROUND_BEGIN => WinAPIThreadPriority::BackgroundModeBegin,
89                winbase::THREAD_MODE_BACKGROUND_END => WinAPIThreadPriority::BackgroundModeEnd,
90                winbase::THREAD_PRIORITY_ABOVE_NORMAL => WinAPIThreadPriority::AboveNormal,
91                winbase::THREAD_PRIORITY_BELOW_NORMAL => WinAPIThreadPriority::BelowNormal,
92                winbase::THREAD_PRIORITY_HIGHEST => WinAPIThreadPriority::Highest,
93                winbase::THREAD_PRIORITY_IDLE => WinAPIThreadPriority::Idle,
94                winbase::THREAD_PRIORITY_LOWEST => WinAPIThreadPriority::Lowest,
95                winbase::THREAD_PRIORITY_NORMAL => WinAPIThreadPriority::Normal,
96                winbase::THREAD_PRIORITY_TIME_CRITICAL => WinAPIThreadPriority::TimeCritical,
97                _ => {
98                    return Err(Error::Priority(
99                        "The value is out of range of allowed values.",
100                    ))
101                }
102            },
103            ThreadPriority::Max => WinAPIThreadPriority::Highest,
104        })
105    }
106}
107
108impl std::convert::TryFrom<DWORD> for WinAPIThreadPriority {
109    type Error = crate::Error;
110
111    fn try_from(priority: DWORD) -> Result<Self, Self::Error> {
112        Ok(match priority {
113            winbase::THREAD_MODE_BACKGROUND_BEGIN => WinAPIThreadPriority::BackgroundModeBegin,
114            winbase::THREAD_MODE_BACKGROUND_END => WinAPIThreadPriority::BackgroundModeEnd,
115            winbase::THREAD_PRIORITY_ABOVE_NORMAL => WinAPIThreadPriority::AboveNormal,
116            winbase::THREAD_PRIORITY_BELOW_NORMAL => WinAPIThreadPriority::BelowNormal,
117            winbase::THREAD_PRIORITY_HIGHEST => WinAPIThreadPriority::Highest,
118            winbase::THREAD_PRIORITY_IDLE => WinAPIThreadPriority::Idle,
119            winbase::THREAD_PRIORITY_LOWEST => WinAPIThreadPriority::Lowest,
120            winbase::THREAD_PRIORITY_NORMAL => WinAPIThreadPriority::Normal,
121            winbase::THREAD_PRIORITY_TIME_CRITICAL => WinAPIThreadPriority::TimeCritical,
122            _ => return Err(Error::Priority("Priority couldn't be parsed")),
123        })
124    }
125}
126
127impl From<WinAPIThreadPriority> for crate::ThreadPriorityOsValue {
128    fn from(p: WinAPIThreadPriority) -> Self {
129        crate::ThreadPriorityOsValue(p as u32)
130    }
131}
132
133/// Sets thread's priority and schedule policy.
134///
135/// * May require privileges
136///
137/// # Usage
138///
139/// Setting thread priority to minimum:
140///
141/// ```rust
142/// use thread_priority::*;
143///
144/// let thread_id = thread_native_id();
145/// assert!(set_thread_priority(thread_id, ThreadPriority::Min).is_ok());
146/// ```
147///
148/// If there's an error, a result of
149/// [`GetLastError`](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) is returned.
150pub fn set_thread_priority(native: ThreadId, priority: ThreadPriority) -> Result<(), Error> {
151    set_winapi_thread_priority(native, WinAPIThreadPriority::try_from(priority)?)
152}
153
154/// Sets thread's priority and schedule policy using WinAPI priority values.
155///
156/// * May require privileges
157///
158/// # Usage
159///
160/// Setting thread priority to minimum:
161///
162/// ```rust
163/// use thread_priority::*;
164///
165/// let thread_id = thread_native_id();
166/// assert!(set_winapi_thread_priority(thread_id, WinAPIThreadPriority::Normal).is_ok());
167/// ```
168///
169/// If there's an error, a result of
170/// [`GetLastError`](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) is returned.
171pub fn set_winapi_thread_priority(
172    native: ThreadId,
173    priority: WinAPIThreadPriority,
174) -> Result<(), Error> {
175    unsafe {
176        if SetThreadPriority(native, priority as c_int) != 0 {
177            Ok(())
178        } else {
179            Err(Error::OS(GetLastError() as i32))
180        }
181    }
182}
183
184/// Set current thread's priority.
185///
186/// * May require privileges
187///
188/// # Usage
189///
190/// Setting thread priority to minimum:
191///
192/// ```rust
193/// use thread_priority::*;
194///
195/// assert!(set_current_thread_priority(ThreadPriority::Min).is_ok());
196/// assert!(set_current_thread_priority(ThreadPriority::Os(WinAPIThreadPriority::Lowest.into())).is_ok());
197/// ```
198///
199/// If there's an error, a result of
200/// [`GetLastError`](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) is returned.
201
202pub fn set_current_thread_priority(priority: ThreadPriority) -> Result<(), Error> {
203    let thread_id = thread_native_id();
204    set_thread_priority(thread_id, priority)
205}
206
207/// Get the thread's priority value.
208///
209/// Returns current thread's priority.
210///
211/// # Usage
212///
213/// ```rust
214/// use thread_priority::{thread_native_id, get_thread_priority};
215///
216/// assert!(get_thread_priority(thread_native_id()).is_ok());
217/// ```
218pub fn get_thread_priority(native: ThreadId) -> Result<ThreadPriority, Error> {
219    unsafe {
220        let ret = GetThreadPriority(native);
221        if ret as u32 != winbase::THREAD_PRIORITY_ERROR_RETURN {
222            Ok(ThreadPriority::Os(crate::ThreadPriorityOsValue(
223                WinAPIThreadPriority::try_from(ret as DWORD)? as u32,
224            )))
225        } else {
226            Err(Error::OS(GetLastError() as i32))
227        }
228    }
229}
230
231/// Get current thread's priority value.
232///
233/// Returns current thread's priority.
234///
235/// # Usage
236///
237/// ```rust
238/// use thread_priority::get_current_thread_priority;
239///
240/// assert!(get_current_thread_priority().is_ok());
241/// ```
242pub fn get_current_thread_priority() -> Result<ThreadPriority, Error> {
243    unsafe {
244        let ret = GetThreadPriority(thread_native_id());
245        if ret as u32 != winbase::THREAD_PRIORITY_ERROR_RETURN {
246            Ok(ThreadPriority::Os(crate::ThreadPriorityOsValue(
247                WinAPIThreadPriority::try_from(ret as DWORD)? as u32,
248            )))
249        } else {
250            Err(Error::OS(GetLastError() as i32))
251        }
252    }
253}
254
255/// Returns current thread id, which is the current OS's native handle.
256/// It may or may not be equal or even related to rust's thread id,
257/// there is absolutely no guarantee for that.
258///
259/// # Usage
260///
261/// ```rust
262/// use thread_priority::thread_native_id;
263///
264/// assert!(!thread_native_id().is_null());
265/// ```
266pub fn thread_native_id() -> ThreadId {
267    unsafe { GetCurrentThread() }
268}
269
270/// Disables or enables the ability of the system to temporarily boost the priority of a thread.
271///
272/// If there's an error, a result of
273/// [`GetLastError`](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) is returned.
274///
275/// # Usage
276///
277/// ```rust
278/// use thread_priority::*;
279///
280/// let thread_id = thread_native_id();
281/// assert!(set_thread_priority_boost(thread_id, false).is_ok())
282/// ```
283pub fn set_thread_priority_boost(native: ThreadId, enabled: bool) -> Result<(), Error> {
284    unsafe {
285        if SetThreadPriorityBoost(native, enabled as i32) != 0 {
286            Ok(())
287        } else {
288            Err(Error::OS(GetLastError() as i32))
289        }
290    }
291}
292
293/// Disables or enables the ability of the system to temporarily boost the priority of a current thread.
294///
295/// If there's an error, a result of
296/// [`GetLastError`](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) is returned.
297///
298/// This is a short-hand of the `set_thread_priority_boost` function for the current thread.
299pub fn set_current_thread_priority_boost(enabled: bool) -> Result<(), Error> {
300    set_thread_priority_boost(thread_native_id(), enabled)
301}
302
303/// Sets a preferred processor for a thread. The system schedules threads on their preferred
304/// processors whenever possible.
305///
306/// On a system with more than 64 processors, this function sets the preferred processor to a
307/// logical processor in the processor group to which the calling thread is assigned. Use the
308/// `SetThreadIdealProcessorEx` function to specify a processor group and preferred processor.
309///
310/// If there's an error, a result of
311/// [`GetLastError`](https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) is returned.
312/// On success, the function returns a previously assigned processor.
313///
314/// # Note
315/// The processor number starts with zero.
316///
317/// # Usage
318///
319/// ```rust
320/// use thread_priority::*;
321///
322/// let thread_id = thread_native_id();
323/// assert!(set_thread_ideal_processor(thread_id, 0).is_ok())
324/// ```
325pub fn set_thread_ideal_processor(
326    native: ThreadId,
327    ideal_processor: IdealProcessor,
328) -> Result<IdealProcessor, Error> {
329    unsafe {
330        let ret = SetThreadIdealProcessor(native, ideal_processor);
331        if ret == IdealProcessor::max_value() - 1 {
332            Err(Error::OS(GetLastError() as i32))
333        } else {
334            Ok(ret)
335        }
336    }
337}
338
339/// Sets a preferred processor for a current thread. The system schedules threads on their preferred
340/// processors whenever possible.
341///
342/// This is a short-hand of the `set_thread_ideal_processor` function for the current thread.
343pub fn set_current_thread_ideal_processor(
344    ideal_processor: IdealProcessor,
345) -> Result<IdealProcessor, Error> {
346    set_thread_ideal_processor(thread_native_id(), ideal_processor)
347}
348
349impl std::convert::TryFrom<u32> for crate::ThreadPriorityOsValue {
350    type Error = ();
351
352    fn try_from(value: u32) -> Result<Self, Self::Error> {
353        Ok(crate::ThreadPriorityOsValue(match value {
354            winbase::THREAD_MODE_BACKGROUND_BEGIN
355            | winbase::THREAD_MODE_BACKGROUND_END
356            | winbase::THREAD_PRIORITY_ABOVE_NORMAL
357            | winbase::THREAD_PRIORITY_BELOW_NORMAL
358            | winbase::THREAD_PRIORITY_HIGHEST
359            | winbase::THREAD_PRIORITY_IDLE
360            | winbase::THREAD_PRIORITY_LOWEST
361            | winbase::THREAD_PRIORITY_NORMAL
362            | winbase::THREAD_PRIORITY_TIME_CRITICAL => value,
363            _ => return Err(()),
364        }))
365    }
366}
367
368/// Windows-specific complemented part of the [`crate::ThreadExt`] trait.
369pub trait ThreadExt {
370    /// Returns current thread's priority.
371    /// For more info see [`thread_priority`].
372    ///
373    /// ```rust
374    /// use thread_priority::*;
375    ///
376    /// assert!(std::thread::current().get_priority().is_ok());
377    /// ```
378    fn get_priority(&self) -> Result<ThreadPriority, Error> {
379        get_current_thread_priority()
380    }
381
382    /// Sets current thread's priority.
383    /// For more info see [`set_current_thread_priority`].
384    ///
385    /// ```rust
386    /// use thread_priority::*;
387    ///
388    /// assert!(std::thread::current().set_priority(ThreadPriority::Min).is_ok());
389    /// ```
390    fn set_priority(&self, priority: ThreadPriority) -> Result<(), Error> {
391        set_current_thread_priority(priority)
392    }
393
394    /// Returns current thread's windows id.
395    /// For more info see [`thread_native_id`].
396    ///
397    /// ```rust
398    /// use thread_priority::*;
399    ///
400    /// assert!(!std::thread::current().get_native_id().unwrap().is_null());
401    /// ```
402    fn get_native_id(&self) -> Result<ThreadId, Error>;
403
404    /// Sets current thread's ideal processor.
405    /// For more info see [`set_current_thread_ideal_processor`].
406    ///
407    /// ```rust
408    /// use thread_priority::*;
409    ///
410    /// assert!(std::thread::current().set_ideal_processor(0).is_ok());
411    /// ```
412    fn set_ideal_processor(
413        &self,
414        ideal_processor: IdealProcessor,
415    ) -> Result<IdealProcessor, Error> {
416        set_current_thread_ideal_processor(ideal_processor)
417    }
418
419    /// Sets current thread's priority boost.
420    /// For more info see [`set_current_thread_priority_boost`].
421    ///
422    /// ```rust
423    /// use thread_priority::*;
424    ///
425    /// assert!(std::thread::current().set_priority_boost(true).is_ok());
426    /// ```
427    fn set_priority_boost(&self, enabled: bool) -> Result<(), Error> {
428        set_current_thread_priority_boost(enabled)
429    }
430}
431
432/// Auto-implementation of this trait for the [`std::thread::Thread`].
433impl ThreadExt for std::thread::Thread {
434    fn get_native_id(&self) -> Result<ThreadId, Error> {
435        if self.id() == std::thread::current().id() {
436            Ok(thread_native_id())
437        } else {
438            Err(Error::Priority("The `ThreadExt::get_native_id()` is currently limited to be called on the current thread."))
439        }
440    }
441}