Skip to content

Commit d3cf28d

Browse files
authored
Merge pull request #12 from RafaelOrtizRC/deadline-support
Add Linux SCHED_DEADLINE support
2 parents d6f9b39 + 0091a06 commit d3cf28d

File tree

5 files changed

+155
-12
lines changed

5 files changed

+155
-12
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ jobs:
5656
if: matrix.features == ''
5757
run: cargo build --all-features
5858

59-
- name: Test all features
60-
if: matrix.features == ''
61-
run: cargo test --all-features
59+
- name: Test all features (other)
60+
if: matrix.features == '' && runner.os != 'Linux'
61+
run: cargo test --all-features -- --skip set_deadline_policy
62+
63+
- name: Test all features (Linux)
64+
if: matrix.features == '' && runner.os == 'Linux'
65+
run: sudo -E /usr/share/rust/.cargo/bin/cargo test --all-features
6266

6367
clippy:
6468
name: Run clippy

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ target/
33
Cargo.lock
44
**/*.swo
55
**/*.swp
6+
.idea/
7+
Vagrantfile
8+
.vagrant/

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "thread-priority"
3-
version = "0.2.4"
3+
version = "0.2.5"
44
authors = ["Victor Polevoy <[email protected]>"]
55
description = "Library for managing threads priority and schedule policies"
66
repository = "https://github.com/vityafx/thread-priority"

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ pub enum ThreadPriority {
4444
/// a percentage value. The `u32` value is reserved for different
4545
/// OS'es support.
4646
Specific(u32),
47+
/// Holds scheduling parameters for Deadline scheduling. These are, in order,
48+
/// the nanoseconds for runtime, deadline, and period. Please note that the
49+
/// kernel enforces runtime <= deadline <= period.
50+
#[cfg(target_os = "linux")]
51+
Deadline(u64, u64, u64),
4752
/// Holds a value representing the maximum possible priority.
4853
/// Should be used with caution, it solely depends on the target
4954
/// os where the program is going to be running on, how it will

src/unix.rs

Lines changed: 139 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,33 @@ pub struct ScheduleParams {
1515
pub sched_priority: libc::c_int,
1616
}
1717

18+
/// Copy of the Linux kernel's sched_attr type
19+
#[repr(C)]
20+
#[derive(Debug, Default)]
21+
#[cfg(target_os = "linux")]
22+
pub struct SchedAttr {
23+
size: u32,
24+
sched_policy: u32,
25+
sched_flags: u64,
26+
27+
/// for SCHED_NORMAL and SCHED_BATCH
28+
sched_nice: i32,
29+
/// for SCHED_FIFO, SCHED_RR
30+
sched_priority: u32,
31+
32+
/// for SCHED_DEADLINE
33+
sched_runtime: u64,
34+
/// for SCHED_DEADLINE
35+
sched_deadline: u64,
36+
/// for SCHED_DEADLINE
37+
sched_period: u64,
38+
39+
/// Utilization hint
40+
sched_util_min: u32,
41+
/// Utilization hint
42+
sched_util_max: u32,
43+
}
44+
1845
impl ScheduleParams {
1946
#[cfg(not(target_env = "musl"))]
2047
fn into_posix(self) -> libc::sched_param {
@@ -57,12 +84,19 @@ pub enum RealtimeThreadSchedulePolicy {
5784
Fifo,
5885
/// A round-robin policy
5986
RoundRobin,
87+
/// A deadline policy. Note, due to Linux expecting a pid_t and not a pthread_t, the given
88+
/// [ThreadId](struct.ThreadId) will be interpreted as a pid_t. This policy is NOT
89+
/// POSIX-compatible, so we only include it for linux targets.
90+
#[cfg(target_os = "linux")]
91+
Deadline,
6092
}
6193
impl RealtimeThreadSchedulePolicy {
6294
fn to_posix(self) -> libc::c_int {
6395
match self {
6496
RealtimeThreadSchedulePolicy::Fifo => 1,
6597
RealtimeThreadSchedulePolicy::RoundRobin => 2,
98+
#[cfg(target_os = "linux")]
99+
RealtimeThreadSchedulePolicy::Deadline => 6,
66100
}
67101
}
68102
}
@@ -122,6 +156,10 @@ impl ThreadSchedulePolicy {
122156
2 => Ok(ThreadSchedulePolicy::Realtime(
123157
RealtimeThreadSchedulePolicy::RoundRobin,
124158
)),
159+
#[cfg(target_os = "linux")]
160+
6 => Ok(ThreadSchedulePolicy::Realtime(
161+
RealtimeThreadSchedulePolicy::Deadline,
162+
)),
125163
_ => Err(Error::Ffi("Can't parse schedule policy from posix")),
126164
}
127165
}
@@ -133,10 +171,20 @@ impl ThreadPriority {
133171
pub fn to_posix(self, policy: ThreadSchedulePolicy) -> Result<libc::c_int, Error> {
134172
let ret = match self {
135173
ThreadPriority::Min => match policy {
174+
// SCHED_DEADLINE doesn't really have a notion of priority, this is an error
175+
#[cfg(target_os = "linux")]
176+
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => Err(
177+
Error::Priority("Deadline scheduling must use deadline priority."),
178+
),
136179
ThreadSchedulePolicy::Realtime(_) => Ok(1),
137180
_ => Ok(0),
138181
},
139182
ThreadPriority::Specific(p) => match policy {
183+
// SCHED_DEADLINE doesn't really have a notion of priority, this is an error
184+
#[cfg(target_os = "linux")]
185+
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => Err(
186+
Error::Priority("Deadline scheduling must use deadline priority."),
187+
),
140188
ThreadSchedulePolicy::Realtime(_) if (p == 0 || p > 99) => {
141189
Err(Error::Priority("The value is out of range [0; 99]"))
142190
}
@@ -146,9 +194,18 @@ impl ThreadPriority {
146194
_ => Ok(p),
147195
},
148196
ThreadPriority::Max => match policy {
197+
// SCHED_DEADLINE doesn't really have a notion of priority, this is an error
198+
#[cfg(target_os = "linux")]
199+
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => Err(
200+
Error::Priority("Deadline scheduling must use deadline priority."),
201+
),
149202
ThreadSchedulePolicy::Realtime(_) => Ok(99),
150203
_ => Ok(0),
151204
},
205+
#[cfg(target_os = "linux")]
206+
ThreadPriority::Deadline(_, _, _) => Err(Error::Priority(
207+
"Deadline is non-POSIX and cannot be converted.",
208+
)),
152209
};
153210
ret.map(|p| p as libc::c_int)
154211
}
@@ -183,9 +240,12 @@ pub fn set_thread_priority_and_policy(
183240
policy: ThreadSchedulePolicy,
184241
) -> Result<(), Error> {
185242
let params = ScheduleParams {
186-
sched_priority: priority.to_posix(policy)?,
243+
sched_priority: match policy {
244+
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => 0,
245+
_ => priority.to_posix(policy)?,
246+
},
187247
};
188-
set_thread_schedule_policy(native, policy, params)
248+
set_thread_schedule_policy(native, policy, params, priority)
189249
}
190250

191251
/// Set current thread's priority.
@@ -211,6 +271,8 @@ pub fn thread_schedule_policy() -> Result<ThreadSchedulePolicy, Error> {
211271
/// Sets thread schedule policy.
212272
///
213273
/// * May require privileges
274+
/// * Deadline policy requires a tid, not a pthread_t, so invoking this while using a deadline
275+
/// policy will interpret the given [ThreadId](struct.ThreadId) as a pid_t (thread tid).
214276
///
215277
/// # Usage
216278
/// ```rust,no_run
@@ -219,20 +281,54 @@ pub fn thread_schedule_policy() -> Result<ThreadSchedulePolicy, Error> {
219281
/// let thread_id = thread_native_id();
220282
/// let policy = ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Fifo);
221283
/// let params = ScheduleParams { sched_priority: 3 as libc::c_int };
222-
/// assert!(set_thread_schedule_policy(thread_id, policy, params).is_ok());
284+
/// let priority = ThreadPriority::Min;
285+
/// assert!(set_thread_schedule_policy(thread_id, policy, params, priority).is_ok());
223286
/// ```
224287
pub fn set_thread_schedule_policy(
225288
native: ThreadId,
226289
policy: ThreadSchedulePolicy,
227290
params: ScheduleParams,
291+
priority: ThreadPriority,
228292
) -> Result<(), Error> {
229293
let params = params.into_posix();
230294
unsafe {
231-
let ret = libc::pthread_setschedparam(
232-
native,
233-
policy.to_posix(),
234-
&params as *const libc::sched_param,
235-
);
295+
let ret = match policy {
296+
// SCHED_DEADLINE policy requires its own syscall
297+
#[cfg(target_os = "linux")]
298+
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline) => {
299+
let (runtime, deadline, period) = match priority {
300+
ThreadPriority::Deadline(r, d, p) => (r, d, p),
301+
_ => {
302+
return Err(Error::Priority(
303+
"Deadline policy given without deadline priority.",
304+
))
305+
}
306+
};
307+
let tid = native as libc::pid_t;
308+
let sched_attr = SchedAttr {
309+
size: std::mem::size_of::<SchedAttr>() as u32,
310+
sched_policy: policy.to_posix() as u32,
311+
312+
sched_runtime: runtime as u64,
313+
sched_deadline: deadline as u64,
314+
sched_period: period as u64,
315+
316+
..Default::default()
317+
};
318+
libc::syscall(
319+
libc::SYS_sched_setattr,
320+
tid,
321+
&sched_attr as *const _,
322+
// we are not setting SCHED_FLAG_RECLAIM nor SCHED_FLAG_DL_OVERRUN
323+
0,
324+
) as i32
325+
}
326+
_ => libc::pthread_setschedparam(
327+
native,
328+
policy.to_posix(),
329+
&params as *const libc::sched_param,
330+
),
331+
};
236332
match ret {
237333
0 => Ok(()),
238334
e => Err(Error::OS(e)),
@@ -328,4 +424,39 @@ mod tests {
328424
)
329425
.is_ok());
330426
}
427+
428+
#[test]
429+
#[cfg(target_os = "linux")]
430+
fn set_deadline_policy() {
431+
// allow the identity operation for clarity
432+
#![allow(clippy::identity_op)]
433+
434+
assert!(set_thread_priority_and_policy(
435+
0, // current thread
436+
ThreadPriority::Deadline(1 * 10_u64.pow(6), 10 * 10_u64.pow(6), 100 * 10_u64.pow(6)),
437+
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Deadline)
438+
)
439+
.is_ok());
440+
441+
// now we check the return values
442+
unsafe {
443+
let mut sched_attr = SchedAttr::default();
444+
let ret = libc::syscall(
445+
libc::SYS_sched_getattr,
446+
0, // current thread
447+
&mut sched_attr as *mut _,
448+
std::mem::size_of::<SchedAttr>() as u32,
449+
0, // flags must be 0
450+
);
451+
452+
assert!(ret >= 0);
453+
assert_eq!(
454+
sched_attr.sched_policy,
455+
RealtimeThreadSchedulePolicy::Deadline.to_posix() as u32
456+
);
457+
assert_eq!(sched_attr.sched_runtime, 1 * 10_u64.pow(6));
458+
assert_eq!(sched_attr.sched_deadline, 10 * 10_u64.pow(6));
459+
assert_eq!(sched_attr.sched_period, 100 * 10_u64.pow(6));
460+
}
461+
}
331462
}

0 commit comments

Comments
 (0)