Skip to content

Commit ede4b7b

Browse files
committed
Add more interfaces.
1. Adds a ThreadBuilderExt trait containing helpful methods for spawning a thread with priority. The trait is seamlessly implemented for the std::thread::Builder so that it is easy to use. 2. Adds a struct ThreadBuilder which can be used to set the priority and stuff much more easily than just calling functions, before starting a thread. Only Linux is supported, the Windows support is coming soon.
1 parent 2db9082 commit ede4b7b

File tree

5 files changed

+390
-41
lines changed

5 files changed

+390
-41
lines changed

.travis.yml

Lines changed: 0 additions & 29 deletions
This file was deleted.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ keywords = ["thread", "schedule", "priority", "pthread"]
1111
categories = ["concurrency", "asynchronous", "os"]
1212
edition = "2018"
1313

14+
[dependencies]
15+
log = "0.4"
16+
1417
[target.'cfg(unix)'.dependencies]
1518
libc = "0.2"
1619

1720
[target.'cfg(windows)'.dependencies]
1821
libc = "0.2"
19-
winapi = { version = "0.3", features = ["errhandlingapi", "processthreadsapi", "winnt", "minwindef", "winbase"] }
22+
winapi = { version = "0.3", features = ["errhandlingapi", "processthreadsapi", "winnt", "minwindef", "winbase"] }

src/lib.rs

Lines changed: 265 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
pub mod unix;
1919
#[cfg(unix)]
2020
pub use unix::*;
21+
2122
#[cfg(windows)]
2223
pub mod windows;
2324
#[cfg(windows)]
@@ -31,19 +32,23 @@ pub enum Error {
3132
/// Target OS' error type. In most systems it is an integer which
3233
/// later should be used with target OS' API for understanding the value.
3334
OS(i32),
34-
/// FFI failure
35+
/// FFI failure.
3536
Ffi(&'static str),
3637
}
3738

39+
/// Platform-specific thread priority value.
40+
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
41+
pub struct ThreadPriorityValue(pub u32);
42+
3843
/// Thread priority enumeration.
39-
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
44+
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
4045
pub enum ThreadPriority {
4146
/// Holds a value representing the minimum possible priority.
4247
Min,
4348
/// Holds a specific priority value. Should be in [0; 100] range,
4449
/// a percentage value. The `u32` value is reserved for different
4550
/// OS'es support.
46-
Specific(u32),
51+
Specific(ThreadPriorityValue),
4752
/// Holds scheduling parameters for Deadline scheduling. These are, in order,
4853
/// the nanoseconds for runtime, deadline, and period. Please note that the
4954
/// kernel enforces runtime <= deadline <= period.
@@ -90,3 +95,260 @@ impl Thread {
9095
})
9196
}
9297
}
98+
99+
/// A copy of the [`std::thread::Builder`] builder allowing to set priority settings.
100+
///
101+
/// ```rust
102+
/// use thread_priority::*;
103+
///
104+
/// let thread = ThreadBuilder::default()
105+
/// .name("MyThread")
106+
/// .priority(ThreadPriority::Max)
107+
/// .spawn(|result| {
108+
/// // This is printed out from within the spawned thread.
109+
/// println!("Set priority result: {:?}", result);
110+
/// assert!(result.is_ok());
111+
/// }).unwrap();
112+
/// thread.join();
113+
///
114+
/// // Another example where we don't care about the priority having been set.
115+
/// let thread = ThreadBuilder::default()
116+
/// .name("MyThread")
117+
/// .priority(ThreadPriority::Max)
118+
/// .spawn_careless(|| {
119+
/// // This is printed out from within the spawned thread.
120+
/// println!("We don't care about the priority result.");
121+
/// }).unwrap();
122+
/// thread.join();
123+
/// ```
124+
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Ord, PartialOrd)]
125+
pub struct ThreadBuilder {
126+
name: Option<String>,
127+
stack_size: Option<usize>,
128+
priority: Option<ThreadPriority>,
129+
130+
#[cfg(unix)]
131+
policy: Option<ThreadSchedulePolicy>,
132+
// #[cfg(windows)]
133+
// winapi_priority: Option<WinAPIThreadPriority>,
134+
// #[cfg(windows)]
135+
// boost_enabled: bool,
136+
// #[cfg(windows)]
137+
// ideal_processor: IdealProcessor,
138+
}
139+
140+
impl ThreadBuilder {
141+
/// Names the thread-to-be. Currently the name is used for identification
142+
/// only in panic messages.
143+
///
144+
/// The name must not contain null bytes (`\0`).
145+
///
146+
/// For more information about named threads, see
147+
/// [`std::thread::Builder::name()`].
148+
pub fn name<VALUE: Into<String>>(mut self, value: VALUE) -> Self {
149+
self.name = Some(value.into());
150+
self
151+
}
152+
153+
/// Sets the size of the stack (in bytes) for the new thread.
154+
///
155+
/// The actual stack size may be greater than this value if
156+
/// the platform specifies a minimal stack size.
157+
///
158+
/// For more information about the stack size for threads, see
159+
/// [`std::thread::Builder::stack_size()`].
160+
pub fn stack_size<VALUE: Into<usize>>(mut self, value: VALUE) -> Self {
161+
self.stack_size = Some(value.into());
162+
self
163+
}
164+
165+
/// The thread's custom priority.
166+
///
167+
/// For more information about the stack size for threads, see
168+
/// [`ThreadPriority`].
169+
pub fn priority<VALUE: Into<ThreadPriority>>(mut self, value: VALUE) -> Self {
170+
self.priority = Some(value.into());
171+
self
172+
}
173+
174+
/// The thread's unix scheduling policy.
175+
///
176+
/// For more information, see
177+
/// [`crate::unix::ThreadSchedulePolicy`] and [`crate::unix::set_thread_priority_and_policy`].
178+
#[cfg(unix)]
179+
pub fn policy<VALUE: Into<unix::ThreadSchedulePolicy>>(mut self, value: VALUE) -> Self {
180+
self.policy = Some(value.into());
181+
self
182+
}
183+
184+
/// Spawns a new thread by taking ownership of the `Builder`, and returns an
185+
/// [`std::io::Result`] to its [`std::thread::JoinHandle`].
186+
///
187+
/// See [`std::thread::Builder::spawn`]
188+
pub fn spawn<F, T>(mut self, f: F) -> std::io::Result<std::thread::JoinHandle<T>>
189+
where
190+
F: FnOnce(Result<(), Error>) -> T,
191+
F: Send + 'static,
192+
T: Send + 'static,
193+
{
194+
let priority = self.priority;
195+
let policy = self.policy;
196+
197+
self.build_std().spawn(move || match (priority, policy) {
198+
(Some(priority), Some(policy)) => f(set_thread_priority_and_policy(
199+
thread_native_id(),
200+
priority,
201+
policy,
202+
)),
203+
(Some(priority), None) => f(priority.set_for_current()),
204+
(None, Some(_policy)) => {
205+
unimplemented!("Setting the policy separately isn't currently supported.");
206+
}
207+
_ => f(Ok(())),
208+
})
209+
}
210+
211+
fn build_std(&mut self) -> std::thread::Builder {
212+
let mut builder = std::thread::Builder::new();
213+
214+
if let Some(name) = &self.name {
215+
builder = builder.name(name.to_owned());
216+
}
217+
218+
if let Some(stack_size) = self.stack_size {
219+
builder = builder.stack_size(stack_size);
220+
}
221+
222+
builder
223+
}
224+
225+
/// Spawns a new thread by taking ownership of the `Builder`, and returns an
226+
/// [`std::io::Result`] to its [`std::thread::JoinHandle`].
227+
///
228+
/// See [`std::thread::Builder::spawn`]
229+
pub fn spawn_careless<F, T>(self, f: F) -> std::io::Result<std::thread::JoinHandle<T>>
230+
where
231+
F: FnOnce() -> T,
232+
F: Send + 'static,
233+
T: Send + 'static,
234+
{
235+
self.spawn(|priority_set_result| {
236+
if let Err(e) = priority_set_result {
237+
log::warn!(
238+
"Couldn't set the priority for the thread with Rust Thread ID {:?} named {:?}: {:?}",
239+
std::thread::current().id(),
240+
std::thread::current().name(),
241+
e,
242+
);
243+
}
244+
245+
f()
246+
})
247+
}
248+
}
249+
250+
/// Adds thread building functions using the priority.
251+
pub trait ThreadBuilderExt {
252+
/// Spawn a thread with set priority. The passed functor `f` is executed in the spawned thread and
253+
/// receives as the only argument the result of setting the thread priority.
254+
/// See [`std::thread::Builder::spawn`] and [`ThreadPriority::set_for_current`] for more info.
255+
///
256+
/// # Example
257+
///
258+
/// ```rust
259+
/// use thread_priority::*;
260+
/// use thread_priority::ThreadBuilderExt;
261+
///
262+
/// let thread = std::thread::Builder::new()
263+
/// .name("MyNewThread".to_owned())
264+
/// .spawn_with_priority(ThreadPriority::Max, |result| {
265+
/// // This is printed out from within the spawned thread.
266+
/// println!("Set priority result: {:?}", result);
267+
/// assert!(result.is_ok());
268+
/// }).unwrap();
269+
/// thread.join();
270+
/// ```
271+
fn spawn_with_priority<F, T>(
272+
self,
273+
priority: ThreadPriority,
274+
f: F,
275+
) -> std::io::Result<std::thread::JoinHandle<T>>
276+
where
277+
F: FnOnce(Result<(), Error>) -> T,
278+
F: Send + 'static,
279+
T: Send + 'static;
280+
}
281+
282+
impl ThreadBuilderExt for std::thread::Builder {
283+
fn spawn_with_priority<F, T>(
284+
self,
285+
priority: ThreadPriority,
286+
f: F,
287+
) -> std::io::Result<std::thread::JoinHandle<T>>
288+
where
289+
F: FnOnce(Result<(), Error>) -> T,
290+
F: Send + 'static,
291+
T: Send + 'static,
292+
{
293+
self.spawn(move || f(priority.set_for_current()))
294+
}
295+
}
296+
297+
/// Spawns a thread with the specified priority.
298+
///
299+
/// See [`ThreadBuilderExt::spawn_with_priority`].
300+
///
301+
/// ```rust
302+
/// use thread_priority::*;
303+
///
304+
/// let thread = spawn(ThreadPriority::Max, |result| {
305+
/// // This is printed out from within the spawned thread.
306+
/// println!("Set priority result: {:?}", result);
307+
/// assert!(result.is_ok());
308+
/// });
309+
/// thread.join();
310+
/// ```
311+
pub fn spawn<F, T>(priority: ThreadPriority, f: F) -> std::thread::JoinHandle<T>
312+
where
313+
F: FnOnce(Result<(), Error>) -> T,
314+
F: Send + 'static,
315+
T: Send + 'static,
316+
{
317+
std::thread::spawn(move || f(priority.set_for_current()))
318+
}
319+
320+
/// Spawns a thread with the specified priority.
321+
/// This is different from [`spawn`] in a way that the passed function doesn't
322+
/// need to accept the [`ThreadPriority::set_for_current`] result.
323+
/// In case of an error, the error is logged using the logging facilities.
324+
///
325+
/// See [`spawn`].
326+
///
327+
/// ```rust
328+
/// use thread_priority::*;
329+
///
330+
/// let thread = spawn_careless(ThreadPriority::Max, || {
331+
/// // This is printed out from within the spawned thread.
332+
/// println!("We don't care about the priority result.");
333+
/// });
334+
/// thread.join();
335+
/// ```
336+
pub fn spawn_careless<F, T>(priority: ThreadPriority, f: F) -> std::thread::JoinHandle<T>
337+
where
338+
F: FnOnce() -> T,
339+
F: Send + 'static,
340+
T: Send + 'static,
341+
{
342+
std::thread::spawn(move || {
343+
if let Err(e) = priority.set_for_current() {
344+
log::warn!(
345+
"Couldn't set the priority for the thread with Rust Thread ID {:?} named {:?}: {:?}",
346+
std::thread::current().id(),
347+
std::thread::current().name(),
348+
e,
349+
);
350+
}
351+
352+
f()
353+
})
354+
}

0 commit comments

Comments
 (0)