thread_priority/
lib.rs

1//! Thread priority. A library for changing thread's priority.
2//!
3//! # Usage
4//!
5//! Setting thread priority to minimum:
6//!
7//! ```rust
8//! use thread_priority::*;
9//!
10//! assert!(set_current_thread_priority(ThreadPriority::Min).is_ok());
11//! // Or like this:
12//! assert!(ThreadPriority::Min.set_for_current().is_ok());
13//! ```
14//!
15//! # More examples
16//!
17//! ### Minimal cross-platform examples
18//! Setting current thread's priority to minimum:
19//!
20//! ```rust,no_run
21//! use thread_priority::*;
22//!
23//! assert!(set_current_thread_priority(ThreadPriority::Min).is_ok());
24//! ```
25//!
26//! The same as above but using a specific value:
27//!
28//! ```rust,no_run
29//! use thread_priority::*;
30//! use std::convert::TryInto;
31//!
32//! // The lower the number the lower the priority.
33//! assert!(set_current_thread_priority(ThreadPriority::Crossplatform(0.try_into().unwrap())).is_ok());
34//! ```
35//!
36//! ### Building a thread using the [`ThreadBuilderExt`] trait
37//!
38//! ```rust,no_run
39//! use thread_priority::*;
40//! use thread_priority::ThreadBuilderExt;
41//!
42//! let thread = std::thread::Builder::new()
43//!     .name("MyNewThread".to_owned())
44//!     .spawn_with_priority(ThreadPriority::Max, |result| {
45//!         // This is printed out from within the spawned thread.
46//!         println!("Set priority result: {:?}", result);
47//!         assert!(result.is_ok());
48//! }).unwrap();
49//! thread.join();
50//!
51//! // This also support scoped thread.
52//! let x = 0;
53//! std::thread::scope(|s|{
54//!     std::thread::Builder::new()
55//!         .name("MyNewThread".to_owned())
56//!         .spawn_scoped_with_priority(s, ThreadPriority::Max, |result| {
57//!             // This is printed out from within the spawned thread.
58//!             println!("Set priority result: {:?}", result);
59//!             assert!(result.is_ok());
60//!             dbg!(&x);
61//!     }).unwrap();
62//! });
63//! ```
64//!
65//! ### Building a thread using the [`ThreadScopeExt`] trait
66//!
67//! ```rust,no_run
68//! use thread_priority::*;
69//! let x = 0;
70//! std::thread::scope(|s|{
71//!     s.spawn_with_priority(ThreadPriority::Max, |result| {
72//!             // This is printed out from within the spawned thread.
73//!             println!("Set priority result: {:?}", result);
74//!             assert!(result.is_ok());
75//!             dbg!(&x);
76//!     });
77//! });
78//! ```
79//!
80//! ### Building a thread using the [`ThreadBuilder`].
81//!
82//! ```rust,no_run
83//! use thread_priority::*;
84//!
85//! let thread = ThreadBuilder::default()
86//!     .name("MyThread")
87//!     .priority(ThreadPriority::Max)
88//!     .spawn(|result| {
89//!         // This is printed out from within the spawned thread.
90//!         println!("Set priority result: {:?}", result);
91//!         assert!(result.is_ok());
92//! }).unwrap();
93//! thread.join();
94//!
95//! // Another example where we don't care about the priority having been set.
96//! let thread = ThreadBuilder::default()
97//!     .name("MyThread")
98//!     .priority(ThreadPriority::Max)
99//!     .spawn_careless(|| {
100//!         // This is printed out from within the spawned thread.
101//!         println!("We don't care about the priority result.");
102//! }).unwrap();
103//! thread.join();
104//!
105//! // Scoped thread is also supported if the compiler version is at least 1.63.
106//! let mut x = 0;
107//! std::thread::scope(|s|{
108//!     let thread = ThreadBuilder::default()
109//!         .name("MyThread")
110//!         .priority(ThreadPriority::Max)
111//!         .spawn_scoped(s, |result| {
112//!             // This is printed out from within the spawned thread.
113//!             println!("Set priority result: {:?}", result);
114//!             assert!(result.is_ok());
115//!             x += 1;
116//!     }).unwrap();
117//!     thread.join();
118//! });
119//! assert_eq!(x, 1);
120//!
121//! // Scoped thread also has a "careless" mode.
122//! std::thread::scope(|s|{
123//!     let thread = ThreadBuilder::default()
124//!         .name("MyThread")
125//!         .priority(ThreadPriority::Max)
126//!         .spawn_scoped_careless(s, || {
127//!             // This is printed out from within the spawned thread.
128//!             println!("We don't care about the priority result.");
129//!             x += 1;
130//!     }).unwrap();
131//!     thread.join();
132//! });
133//! assert_eq!(x, 2);
134//! ```
135//!
136//! ### Using [`ThreadExt`] trait on the current thread
137//!
138//! ```rust,no_run
139//! use thread_priority::*;
140//!
141//! assert!(std::thread::current().get_priority().is_ok());
142//! println!("This thread's native id is: {:?}", std::thread::current().get_native_id());
143//! ```
144//!
145#![warn(missing_docs)]
146#![deny(warnings)]
147
148#[cfg(any(
149    target_os = "linux",
150    target_os = "macos",
151    target_os = "ios",
152    target_os = "dragonfly",
153    target_os = "freebsd",
154    target_os = "openbsd",
155    target_os = "vxworks",
156    target_os = "netbsd",
157    target_os = "android",
158    target_arch = "wasm32",
159))]
160pub mod unix;
161#[cfg(any(target_os = "linux", target_os = "android"))]
162use std::time::Duration;
163
164#[cfg(any(
165    target_os = "linux",
166    target_os = "macos",
167    target_os = "ios",
168    target_os = "dragonfly",
169    target_os = "freebsd",
170    target_os = "openbsd",
171    target_os = "vxworks",
172    target_os = "netbsd",
173    target_os = "android",
174    target_arch = "wasm32",
175))]
176pub use unix::*;
177
178#[cfg(windows)]
179pub mod windows;
180#[cfg(windows)]
181pub use windows::*;
182
183/// A error type
184#[derive(Debug, Clone, Eq, PartialEq, Hash)]
185pub enum Error {
186    /// A value which describes why it is impossible to use such a priority.
187    Priority(&'static str),
188    /// Indicates that the priority isn't in range and it should be within the provided range.
189    /// This may happen on different operating systems following a single standard of API but
190    /// allowing different priority values for different scheduling policies.
191    PriorityNotInRange(std::ops::RangeInclusive<i32>),
192    /// Target OS' error type. In most systems it is an integer which
193    /// later should be used with target OS' API for understanding the value.
194    /// On Linux there is an integer containing an error code from errno.
195    /// For Windows it contains a number used in Windows for the same purpose.
196    OS(i32),
197    /// FFI failure.
198    Ffi(&'static str),
199}
200
201impl std::fmt::Display for Error {
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        match self {
204            Error::Priority(s) => write!(f, "unable to set priority: {s}"),
205            Error::PriorityNotInRange(range) => {
206                write!(f, "priority must be within the range: {range:?}")
207            }
208            Error::OS(i) => write!(f, "the operating system returned error code {i}"),
209            Error::Ffi(s) => write!(f, "FFI error: {s}"),
210        }
211    }
212}
213
214impl std::error::Error for Error {}
215
216/// Platform-independent thread priority value.
217/// Should be in `[0; 100)` range. The higher the number is - the higher
218/// the priority.
219///
220/// The only way to create such a value is a safe conversion from an 8-byte
221/// unsigned integer ([`u8`]):
222///
223/// ```rust
224/// use thread_priority::*;
225/// use std::convert::{TryFrom, TryInto};
226///
227/// // Create the lowest possible priority value.
228/// assert!(ThreadPriorityValue::try_from(0u8).is_ok());
229/// // Create it implicitly via `TryInto`:
230/// let _priority = ThreadPriority::Crossplatform(0u8.try_into().unwrap());
231/// ```
232///
233/// In case you need to get the raw value out of it, use the `Into<u8>` trait:
234///
235/// ```rust
236/// use thread_priority::*;
237/// use std::convert::TryFrom;
238///
239/// // Create the lowest possible priority value.
240/// let priority = ThreadPriorityValue::try_from(0u8).unwrap();
241/// // Create it implicitly via `TryInto`:
242/// let raw_value: u8 = priority.into();
243/// assert_eq!(raw_value, 0);
244/// ```
245#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
246pub struct ThreadPriorityValue(u8);
247impl ThreadPriorityValue {
248    /// The maximum value for a thread priority.
249    pub const MAX: u8 = if cfg!(target_os = "vxworks") { 255 } else { 99 };
250    /// The minimum value for a thread priority.
251    pub const MIN: u8 = 0;
252}
253
254impl std::convert::TryFrom<u8> for ThreadPriorityValue {
255    type Error = String;
256
257    fn try_from(value: u8) -> Result<Self, Self::Error> {
258        if (Self::MIN..=Self::MAX).contains(&value) {
259            Ok(Self(value))
260        } else {
261            Err(format!(
262                "The value is not in the range of [{}; {}]",
263                Self::MIN,
264                Self::MAX
265            ))
266        }
267    }
268}
269
270// The From<u8> is unsafe, so there is a TryFrom instead.
271// For this reason we silent the warning from clippy.
272#[allow(clippy::from_over_into)]
273impl std::convert::Into<u8> for ThreadPriorityValue {
274    fn into(self) -> u8 {
275        self.0
276    }
277}
278
279/// Platform-specific thread priority value.
280#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
281pub struct ThreadPriorityOsValue(u32);
282
283/// Thread priority enumeration.
284#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
285pub enum ThreadPriority {
286    /// Holds a value representing the minimum possible priority.
287    #[cfg_attr(
288        target_os = "windows",
289        doc = "\
290The [`ThreadPriority::Min`] value is mapped to [`WinAPIThreadPriority::Lowest`] and not
291[`WinAPIThreadPriority::Idle`] to avoid unexpected drawbacks. Use the specific value
292to set it to [`WinAPIThreadPriority::Idle`] when it is really needed.
293"
294    )]
295    Min,
296    /// Holds a platform-independent priority value.
297    /// Usually used when setting a value, for sometimes it is not possible to map
298    /// the operating system's priority to this value.
299    Crossplatform(ThreadPriorityValue),
300    /// Holds an operating system specific value. If it is not possible to obtain the
301    /// [`ThreadPriority::Crossplatform`] variant of the value, this is returned instead.
302    #[cfg_attr(
303        target_os = "windows",
304        doc = "\
305The value is matched among possible values in Windows from [`WinAPIThreadPriority::Idle`] till
306[`WinAPIThreadPriority::TimeCritical`]. This is due to windows only having from 7 to 9 possible
307thread priorities and not `100` as it is allowed to have in the [`ThreadPriority::Crossplatform`]
308variant.
309"
310    )]
311    Os(ThreadPriorityOsValue),
312    /// Holds scheduling parameters for Deadline scheduling. These are, in order,
313    /// the nanoseconds for runtime, deadline, and period. Please note that the
314    /// kernel enforces runtime <= deadline <= period.
315    ///
316    ///   arrival/wakeup                    absolute deadline
317    ///        |    start time                    |
318    ///        |        |                         |
319    ///        v        v                         v
320    ///   -----x--------xooooooooooooooooo--------x--------x---
321    ///                 |<-- Runtime ------->|
322    ///        |<----------- Deadline ----------->|
323    ///        |<-------------- Period ------------------->|
324    #[cfg(any(target_os = "linux", target_os = "android"))]
325    Deadline {
326        /// Set this to something larger than the average computation time
327        /// or to the worst-case computation time for hard real-time tasks.
328        runtime: Duration,
329        /// Set this to the relative deadline.
330        deadline: Duration,
331        /// Set this to the period of the task.
332        period: Duration,
333        /// Deadline flags.
334        flags: crate::unix::DeadlineFlags,
335    },
336    /// Holds a value representing the maximum possible priority.
337    /// Should be used with caution, it solely depends on the target
338    /// os where the program is going to be running on, how it will
339    /// behave. On some systems, the whole system may become frozen
340    /// if not used properly.
341    #[cfg_attr(
342        target_os = "windows",
343        doc = "\
344The [`ThreadPriority::Max`] value is mapped to [`WinAPIThreadPriority::Highest`] and not
345[`WinAPIThreadPriority::TimeCritical`] to avoid unexpected drawbacks. Use the specific value
346to set it to [`WinAPIThreadPriority::TimeCritical`] when it is really needed.
347"
348    )]
349    Max,
350}
351
352impl ThreadPriority {
353    /// Sets current thread's priority to this value.
354    pub fn set_for_current(self) -> Result<(), Error> {
355        set_current_thread_priority(self)
356    }
357}
358
359/// Represents an OS thread.
360#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
361pub struct Thread {
362    /// Thread's priority.
363    pub priority: ThreadPriority,
364    /// Thread's ID (or handle).
365    pub id: ThreadId,
366}
367
368impl Thread {
369    /// Get current thread.
370    ///
371    /// # Usage
372    ///
373    /// ```rust
374    /// use thread_priority::*;
375    ///
376    /// assert!(Thread::current().is_ok());
377    /// ```
378    pub fn current() -> Result<Thread, Error> {
379        Ok(Thread {
380            priority: get_current_thread_priority()?,
381            id: thread_native_id(),
382        })
383    }
384}
385
386/// A wrapper producing a closure where the input priority set result is logged on error, but no other handling is performed
387fn careless_wrapper<F, T>(f: F) -> impl FnOnce(Result<(), Error>) -> T
388where
389    F: FnOnce() -> T + Send,
390    T: Send,
391{
392    |priority_set_result| {
393        if let Err(e) = priority_set_result {
394            log::warn!(
395                "Couldn't set the priority for the thread with Rust Thread ID {:?} named {:?}: {:?}",
396                std::thread::current().id(),
397                std::thread::current().name(),
398                e,
399            );
400        }
401
402        f()
403    }
404}
405
406/// A copy of the [`std::thread::Builder`] builder allowing to set priority settings.
407///
408/// ```rust
409/// use thread_priority::*;
410///
411/// let thread = ThreadBuilder::default()
412///     .name("MyThread")
413///     .priority(ThreadPriority::Max)
414///     .spawn(|result| {
415///         // This is printed out from within the spawned thread.
416///         println!("Set priority result: {:?}", result);
417///         assert!(result.is_ok());
418/// }).unwrap();
419/// thread.join();
420///
421/// // Another example where we don't care about the priority having been set.
422/// let thread = ThreadBuilder::default()
423///     .name("MyThread")
424///     .priority(ThreadPriority::Max)
425///     .spawn_careless(|| {
426///         // This is printed out from within the spawned thread.
427///         println!("We don't care about the priority result.");
428/// }).unwrap();
429/// thread.join();
430/// ```
431///
432/// If the compiler version is at least 1.63, the scoped thread support is also enabled.
433///
434/// ```rust
435/// use thread_priority::*;
436///
437/// // Scoped thread is also supported if the compiler version is at least 1.63.
438/// let mut x = 0;
439/// std::thread::scope(|s|{
440///     let thread = ThreadBuilder::default()
441///         .name("MyThread")
442///         .priority(ThreadPriority::Max)
443///         .spawn_scoped(s, |result| {
444///             // This is printed out from within the spawned thread.
445///             println!("Set priority result: {:?}", result);
446///             assert!(result.is_ok());
447///             x += 1;
448///     }).unwrap();
449///     thread.join();
450/// });
451/// assert_eq!(x, 1);
452///
453/// // Scoped thread also has a "careless" mode.
454/// std::thread::scope(|s|{
455///     let thread = ThreadBuilder::default()
456///         .name("MyThread")
457///         .priority(ThreadPriority::Max)
458///         .spawn_scoped_careless(s, || {
459///             // This is printed out from within the spawned thread.
460///             println!("We don't care about the priority result.");
461///             x += 1;
462///     }).unwrap();
463///     thread.join();
464/// });
465/// assert_eq!(x, 2);
466/// ```
467#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Ord, PartialOrd)]
468pub struct ThreadBuilder {
469    name: Option<String>,
470    stack_size: Option<usize>,
471    priority: Option<ThreadPriority>,
472
473    #[cfg(unix)]
474    policy: Option<ThreadSchedulePolicy>,
475
476    #[cfg(windows)]
477    winapi_priority: Option<WinAPIThreadPriority>,
478    #[cfg(windows)]
479    boost_enabled: bool,
480    #[cfg(windows)]
481    ideal_processor: Option<IdealProcessor>,
482}
483
484impl ThreadBuilder {
485    /// Names the thread-to-be. Currently the name is used for identification
486    /// only in panic messages.
487    ///
488    /// The name must not contain null bytes (`\0`).
489    ///
490    /// For more information about named threads, see
491    /// [`std::thread::Builder::name()`].
492    pub fn name<VALUE: Into<String>>(mut self, value: VALUE) -> Self {
493        self.name = Some(value.into());
494        self
495    }
496
497    /// Sets the size of the stack (in bytes) for the new thread.
498    ///
499    /// The actual stack size may be greater than this value if
500    /// the platform specifies a minimal stack size.
501    ///
502    /// For more information about the stack size for threads, see
503    /// [`std::thread::Builder::stack_size()`].
504    pub fn stack_size<VALUE: Into<usize>>(mut self, value: VALUE) -> Self {
505        self.stack_size = Some(value.into());
506        self
507    }
508
509    /// The thread's custom priority.
510    ///
511    /// For more information about the stack size for threads, see
512    /// [`ThreadPriority`].
513    pub fn priority<VALUE: Into<ThreadPriority>>(mut self, value: VALUE) -> Self {
514        self.priority = Some(value.into());
515        self
516    }
517
518    /// The thread's unix scheduling policy.
519    ///
520    /// For more information, see
521    /// [`crate::unix::ThreadSchedulePolicy`] and [`crate::unix::set_thread_priority_and_policy`].
522    #[cfg(unix)]
523    pub fn policy<VALUE: Into<unix::ThreadSchedulePolicy>>(mut self, value: VALUE) -> Self {
524        self.policy = Some(value.into());
525        self
526    }
527
528    /// The WinAPI priority representation.
529    ///
530    /// For more information, see
531    /// [`crate::windows::WinAPIThreadPriority`].
532    #[cfg(windows)]
533    pub fn winapi_priority<VALUE: Into<windows::WinAPIThreadPriority>>(
534        mut self,
535        value: VALUE,
536    ) -> Self {
537        self.winapi_priority = Some(value.into());
538        self
539    }
540
541    /// Disables or enables the ability of the system to temporarily boost the priority of a thread.
542    ///
543    /// For more information, see
544    /// [`crate::windows::set_thread_priority_boost`].
545    #[cfg(windows)]
546    pub fn boost_enabled(mut self, value: bool) -> Self {
547        self.boost_enabled = value;
548        self
549    }
550
551    /// Sets a preferred processor for a thread. The system schedules threads on their preferred
552    /// processors whenever possible.
553    ///
554    /// For more information, see
555    /// [`crate::windows::set_thread_ideal_processor`].
556    #[cfg(windows)]
557    pub fn ideal_processor<VALUE: Into<windows::IdealProcessor>>(mut self, value: VALUE) -> Self {
558        self.ideal_processor = Some(value.into());
559        self
560    }
561
562    #[cfg(unix)]
563    fn spawn_wrapper<F, T>(self, f: F) -> impl FnOnce() -> T
564    where
565        F: FnOnce(Result<(), Error>) -> T,
566        F: Send,
567        T: Send,
568    {
569        move || match (self.priority, self.policy) {
570            (Some(priority), Some(policy)) => f(set_thread_priority_and_policy(
571                thread_native_id(),
572                priority,
573                policy,
574            )),
575            (Some(priority), None) => f(priority.set_for_current()),
576            (None, Some(_policy)) => {
577                unimplemented!("Setting the policy separately isn't currently supported.");
578            }
579            _ => f(Ok(())),
580        }
581    }
582
583    #[cfg(windows)]
584    fn spawn_wrapper<F, T>(self, f: F) -> impl FnOnce() -> T
585    where
586        F: FnOnce(Result<(), Error>) -> T,
587        F: Send,
588        T: Send,
589    {
590        move || {
591            let mut result = match (self.priority, self.winapi_priority) {
592                (Some(priority), None) => set_thread_priority(thread_native_id(), priority),
593                (_, Some(priority)) => set_winapi_thread_priority(thread_native_id(), priority),
594                _ => Ok(()),
595            };
596            if result.is_ok() && self.boost_enabled {
597                result = set_current_thread_priority_boost(self.boost_enabled);
598            }
599            if result.is_ok() {
600                if let Some(ideal_processor) = self.ideal_processor {
601                    result = set_current_thread_ideal_processor(ideal_processor).map(|_| ());
602                }
603            }
604            f(result)
605        }
606    }
607
608    /// Spawns a new thread by taking ownership of the `Builder`, and returns an
609    /// [`std::io::Result`] to its [`std::thread::JoinHandle`].
610    ///
611    /// See [`std::thread::Builder::spawn`]
612    pub fn spawn<F, T>(mut self, f: F) -> std::io::Result<std::thread::JoinHandle<T>>
613    where
614        F: FnOnce(Result<(), Error>) -> T,
615        F: Send + 'static,
616        T: Send + 'static,
617    {
618        self.build_std().spawn(self.spawn_wrapper(f))
619    }
620
621    /// Spawns a new scoped thread by taking ownership of the `Builder`, and returns an
622    /// [`std::io::Result`] to its [`std::thread::ScopedJoinHandle`].
623    ///
624    /// See [`std::thread::Builder::spawn_scoped`]
625    #[rustversion::since(1.63)]
626    pub fn spawn_scoped<'scope, 'env, F, T>(
627        mut self,
628        scope: &'scope std::thread::Scope<'scope, 'env>,
629        f: F,
630    ) -> std::io::Result<std::thread::ScopedJoinHandle<'scope, T>>
631    where
632        F: FnOnce(Result<(), Error>) -> T,
633        F: Send + 'scope,
634        T: Send + 'scope,
635    {
636        self.build_std().spawn_scoped(scope, self.spawn_wrapper(f))
637    }
638
639    fn build_std(&mut self) -> std::thread::Builder {
640        let mut builder = std::thread::Builder::new();
641
642        if let Some(name) = &self.name {
643            builder = builder.name(name.to_owned());
644        }
645
646        if let Some(stack_size) = self.stack_size {
647            builder = builder.stack_size(stack_size);
648        }
649
650        builder
651    }
652
653    /// Spawns a new thread by taking ownership of the `Builder`, and returns an
654    /// [`std::io::Result`] to its [`std::thread::JoinHandle`].
655    ///
656    /// See [`std::thread::Builder::spawn`]
657    pub fn spawn_careless<F, T>(self, f: F) -> std::io::Result<std::thread::JoinHandle<T>>
658    where
659        F: FnOnce() -> T,
660        F: Send + 'static,
661        T: Send + 'static,
662    {
663        self.spawn(careless_wrapper(f))
664    }
665
666    /// Spawns a new scoped thread by taking ownership of the `Builder`, and returns an
667    /// [`std::io::Result`] to its [`std::thread::ScopedJoinHandle`].
668    ///
669    /// See [`std::thread::Builder::spawn_scoped`]
670    #[rustversion::since(1.63)]
671    pub fn spawn_scoped_careless<'scope, 'env, F, T>(
672        self,
673        scope: &'scope std::thread::Scope<'scope, 'env>,
674        f: F,
675    ) -> std::io::Result<std::thread::ScopedJoinHandle<'scope, T>>
676    where
677        F: FnOnce() -> T,
678        F: Send + 'scope,
679        T: Send + 'scope,
680    {
681        self.spawn_scoped(scope, careless_wrapper(f))
682    }
683}
684
685/// Adds thread building functions using the priority.
686pub trait ThreadBuilderExt {
687    /// Spawn a thread with set priority. The passed functor `f` is executed in the spawned thread and
688    /// receives as the only argument the result of setting the thread priority.
689    /// See [`std::thread::Builder::spawn`] and [`ThreadPriority::set_for_current`] for more info.
690    ///
691    /// # Example
692    ///
693    /// ```rust
694    /// use thread_priority::*;
695    /// use thread_priority::ThreadBuilderExt;
696    ///
697    /// let thread = std::thread::Builder::new()
698    ///     .name("MyNewThread".to_owned())
699    ///     .spawn_with_priority(ThreadPriority::Max, |result| {
700    ///         // This is printed out from within the spawned thread.
701    ///         println!("Set priority result: {:?}", result);
702    ///         assert!(result.is_ok());
703    /// }).unwrap();
704    /// thread.join();
705    /// ```
706    fn spawn_with_priority<F, T>(
707        self,
708        priority: ThreadPriority,
709        f: F,
710    ) -> std::io::Result<std::thread::JoinHandle<T>>
711    where
712        F: FnOnce(Result<(), Error>) -> T,
713        F: Send + 'static,
714        T: Send + 'static;
715
716    /// Spawn a scoped thread with set priority. The passed functor `f` is executed in the spawned thread and
717    /// receives as the only argument the result of setting the thread priority.
718    /// See [`std::thread::Builder::spawn_scoped`] and [`ThreadPriority::set_for_current`] for more info.
719    ///
720    /// # Example
721    ///
722    /// ```rust
723    /// use thread_priority::*;
724    /// use thread_priority::ThreadBuilderExt;
725    ///
726    /// let x = 0;
727    ///
728    /// std::thread::scope(|s|{
729    ///     std::thread::Builder::new()
730    ///         .name("MyNewThread".to_owned())
731    ///         .spawn_scoped_with_priority(s, ThreadPriority::Max, |result| {
732    ///             // This is printed out from within the spawned thread.
733    ///             println!("Set priority result: {:?}", result);
734    ///             assert!(result.is_ok());
735    ///             dbg!(&x);
736    ///     }).unwrap();
737    /// });
738    /// ```
739    #[rustversion::since(1.63)]
740    fn spawn_scoped_with_priority<'scope, 'env, F, T>(
741        self,
742        scope: &'scope std::thread::Scope<'scope, 'env>,
743        priority: ThreadPriority,
744        f: F,
745    ) -> std::io::Result<std::thread::ScopedJoinHandle<'scope, T>>
746    where
747        F: FnOnce(Result<(), Error>) -> T,
748        F: Send + 'scope,
749        T: Send + 'scope;
750}
751
752impl ThreadBuilderExt for std::thread::Builder {
753    fn spawn_with_priority<F, T>(
754        self,
755        priority: ThreadPriority,
756        f: F,
757    ) -> std::io::Result<std::thread::JoinHandle<T>>
758    where
759        F: FnOnce(Result<(), Error>) -> T,
760        F: Send + 'static,
761        T: Send + 'static,
762    {
763        self.spawn(move || f(priority.set_for_current()))
764    }
765
766    #[rustversion::since(1.63)]
767    fn spawn_scoped_with_priority<'scope, 'env, F, T>(
768        self,
769        scope: &'scope std::thread::Scope<'scope, 'env>,
770        priority: ThreadPriority,
771        f: F,
772    ) -> std::io::Result<std::thread::ScopedJoinHandle<'scope, T>>
773    where
774        F: FnOnce(Result<(), Error>) -> T,
775        F: Send + 'scope,
776        T: Send + 'scope,
777    {
778        self.spawn_scoped(scope, move || f(priority.set_for_current()))
779    }
780}
781
782/// Adds scoped thread building functions using the priority.
783#[rustversion::since(1.63)]
784pub trait ThreadScopeExt<'scope> {
785    /// Spawn a scoped thread with set priority. The passed functor `f` is executed in the spawned thread and
786    /// receives as the only argument the result of setting the thread priority.
787    /// See [`std::thread::Scope::spawn`] and [`ThreadPriority::set_for_current`] for more info.
788    ///
789    /// # Example
790    ///
791    /// ```rust
792    /// use thread_priority::*;
793    ///
794    /// let x = 0;
795    ///
796    /// std::thread::scope(|s|{
797    ///     s.spawn_with_priority(ThreadPriority::Max, |result| {
798    ///             // This is printed out from within the spawned thread.
799    ///             println!("Set priority result: {:?}", result);
800    ///             assert!(result.is_ok());
801    ///             dbg!(&x);
802    ///     });
803    /// });
804    /// ```
805    fn spawn_with_priority<F, T>(
806        &'scope self,
807        priority: ThreadPriority,
808        f: F,
809    ) -> std::thread::ScopedJoinHandle<'scope, T>
810    where
811        F: FnOnce(Result<(), Error>) -> T,
812        F: Send + 'scope,
813        T: Send + 'scope;
814}
815
816#[rustversion::since(1.63)]
817impl<'scope> ThreadScopeExt<'scope> for std::thread::Scope<'scope, '_> {
818    fn spawn_with_priority<F, T>(
819        &'scope self,
820        priority: ThreadPriority,
821        f: F,
822    ) -> std::thread::ScopedJoinHandle<'scope, T>
823    where
824        F: FnOnce(Result<(), Error>) -> T,
825        F: Send + 'scope,
826        T: Send + 'scope,
827    {
828        self.spawn(move || f(priority.set_for_current()))
829    }
830}
831
832/// Spawns a thread with the specified priority.
833///
834/// See [`ThreadBuilderExt::spawn_with_priority`].
835///
836/// ```rust
837/// use thread_priority::*;
838///
839/// let thread = spawn(ThreadPriority::Max, |result| {
840///     // This is printed out from within the spawned thread.
841///     println!("Set priority result: {:?}", result);
842///     assert!(result.is_ok());
843/// });
844/// thread.join();
845/// ```
846pub fn spawn<F, T>(priority: ThreadPriority, f: F) -> std::thread::JoinHandle<T>
847where
848    F: FnOnce(Result<(), Error>) -> T,
849    F: Send + 'static,
850    T: Send + 'static,
851{
852    std::thread::spawn(move || f(priority.set_for_current()))
853}
854
855/// Spawns a scoped thread with the specified priority.
856///
857/// See [`ThreadBuilderExt::spawn_with_priority`].
858///
859/// ```rust
860/// use thread_priority::*;
861///
862/// let x = 0;
863///
864/// std::thread::scope(|s| {
865///     spawn_scoped(s, ThreadPriority::Max, |result| {
866///         // This is printed out from within the spawned thread.
867///         println!("Set priority result: {:?}", result);
868///         assert!(result.is_ok());
869///         dbg!(&x);
870///     });
871/// });
872/// ```
873#[rustversion::since(1.63)]
874pub fn spawn_scoped<'scope, 'env, F, T>(
875    scope: &'scope std::thread::Scope<'scope, 'env>,
876    priority: ThreadPriority,
877    f: F,
878) -> std::io::Result<std::thread::ScopedJoinHandle<'scope, T>>
879where
880    F: FnOnce(Result<(), Error>) -> T,
881    F: Send + 'scope,
882    T: Send + 'scope,
883{
884    Ok(scope.spawn(move || f(priority.set_for_current())))
885}
886
887/// Spawns a thread with the specified priority.
888/// This is different from [`spawn`] in a way that the passed function doesn't
889/// need to accept the [`ThreadPriority::set_for_current`] result.
890/// In case of an error, the error is logged using the logging facilities.
891///
892/// See [`spawn`].
893///
894/// ```rust
895/// use thread_priority::*;
896///
897/// let thread = spawn_careless(ThreadPriority::Max, || {
898///     // This is printed out from within the spawned thread.
899///     println!("We don't care about the priority result.");
900/// });
901/// thread.join();
902/// ```
903pub fn spawn_careless<F, T>(priority: ThreadPriority, f: F) -> std::thread::JoinHandle<T>
904where
905    F: FnOnce() -> T,
906    F: Send + 'static,
907    T: Send + 'static,
908{
909    std::thread::spawn(move || careless_wrapper(f)(priority.set_for_current()))
910}
911
912/// Spawns a scoped thread with the specified priority.
913/// This is different from [`spawn_scoped`] in a way that the passed function doesn't
914/// need to accept the [`ThreadPriority::set_for_current`] result.
915/// In case of an error, the error is logged using the logging facilities.
916///
917/// See [`spawn_scoped`].
918///
919/// ```rust
920/// use thread_priority::*;
921///
922/// let x = 0;
923///
924/// std::thread::scope(|s| {
925///     spawn_scoped_careless(s, ThreadPriority::Max, || {
926///         // This is printed out from within the spawned thread.
927///         println!("We don't care about the priority result.");
928///         dbg!(&x);
929///     });
930/// });
931/// ```
932#[rustversion::since(1.63)]
933pub fn spawn_scoped_careless<'scope, 'env, F, T>(
934    scope: &'scope std::thread::Scope<'scope, 'env>,
935    priority: ThreadPriority,
936    f: F,
937) -> std::io::Result<std::thread::ScopedJoinHandle<'scope, T>>
938where
939    F: FnOnce() -> T,
940    F: Send + 'scope,
941    T: Send + 'scope,
942{
943    Ok(scope.spawn(move || careless_wrapper(f)(priority.set_for_current())))
944}