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}