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}