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