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}