1use crate::sched::PreemptRtError::{PriorityAboveMax, PriorityBelowMin};
2use libc::{c_int, pid_t};
3use std::fmt;
4use thiserror::Error;
5
6pub type RtResult<T> = Result<T, PreemptRtError>;
8
9#[derive(Debug, Error)]
10pub enum PreemptRtError {
11 #[error("c function returned errno: {0}")]
12 Errno(c_int),
13 #[error("unknown scheduler for value {0}")]
14 UnknownScheduler(c_int),
15 #[error("priority {0} is higher than max priority {1}")]
16 PriorityAboveMax(c_int, c_int),
17 #[error("priority {0} is lower than min priority {1}")]
18 PriorityBelowMin(c_int, c_int),
19 #[error("current platform {0} does not support preempt-rt")]
20 NonLinuxPlatform(&'static str),
21}
22
23#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
24pub struct Pid(pid_t);
25
26impl Pid {
27 pub const fn current_thread() -> Self {
28 Pid(0)
29 }
30}
31
32impl From<Pid> for pid_t {
33 fn from(pid: Pid) -> Self {
34 pid.0
35 }
36}
37
38impl fmt::Display for Pid {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 fmt::Display::fmt(&self.0, f)
41 }
42}
43
44#[repr(i32)]
45#[allow(non_camel_case_types)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
47pub enum Scheduler {
51 #[cfg(target_os = "linux")]
53 SCHED_NORMAL = libc::SCHED_NORMAL,
54 #[cfg(target_os = "macos")]
56 SCHED_NORMAL = libc::SCHED_OTHER,
57 SCHED_FIFO = libc::SCHED_FIFO,
61 SCHED_RR = libc::SCHED_RR,
63 #[cfg(target_os = "linux")]
67 SCHED_BATCH = libc::SCHED_BATCH,
68 #[cfg(target_os = "linux")]
71 SCHED_IDLE = libc::SCHED_IDLE,
72 #[cfg(target_os = "linux")]
76 SCHED_DEADLINE = libc::SCHED_DEADLINE,
77}
78
79impl TryFrom<c_int> for Scheduler {
80 type Error = PreemptRtError;
81
82 fn try_from(value: c_int) -> RtResult<Self> {
83 match value {
84 #[cfg(target_os = "linux")]
85 libc::SCHED_NORMAL => Ok(Scheduler::SCHED_NORMAL),
86 #[cfg(target_os = "macos")]
87 libc::SCHED_OTHER => Ok(Scheduler::SCHED_NORMAL),
88 libc::SCHED_FIFO => Ok(Scheduler::SCHED_FIFO),
89 libc::SCHED_RR => Ok(Scheduler::SCHED_RR),
90 #[cfg(target_os = "linux")]
91 libc::SCHED_BATCH => Ok(Scheduler::SCHED_BATCH),
92 #[cfg(target_os = "linux")]
93 libc::SCHED_IDLE => Ok(Scheduler::SCHED_IDLE),
94 #[cfg(target_os = "linux")]
95 libc::SCHED_DEADLINE => Ok(Scheduler::SCHED_DEADLINE),
96 _ => Err(PreemptRtError::UnknownScheduler(value)),
97 }
98 }
99}
100
101fn handle_errno(result: c_int) -> RtResult<c_int> {
102 if result == -1 {
103 #[cfg(target_os = "linux")]
104 return Err(PreemptRtError::Errno(unsafe { *libc::__errno_location() }));
105 #[cfg(target_os = "macos")]
106 return Err(PreemptRtError::Errno(unsafe { *libc::__error() }));
107 } else {
108 Ok(result)
109 }
110}
111
112impl Scheduler {
113 pub fn priority_max(&self) -> RtResult<c_int> {
115 let res = unsafe { libc::sched_get_priority_max(*self as c_int) };
116 handle_errno(res)
117 }
118
119 pub fn priority_min(&self) -> RtResult<c_int> {
121 let res = unsafe { libc::sched_get_priority_min(*self as c_int) };
122 handle_errno(res)
123 }
124
125 pub fn with_priority(self, priority: c_int) -> RtResult<ParameterizedScheduler> {
126 let max = self.priority_max()?;
127 let min = self.priority_min()?;
128 if priority > max {
129 Err(PriorityAboveMax(priority, max))
130 } else if priority < min {
131 Err(PriorityBelowMin(priority, min))
132 } else {
133 Ok(ParameterizedScheduler {
134 scheduler: self,
135 params: SchedulerParams { priority },
136 })
137 }
138 }
139}
140
141#[repr(C)]
142#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
143pub struct SchedulerParams {
146 pub priority: c_int,
148}
149
150#[cfg(target_os = "linux")]
151impl From<SchedulerParams> for libc::sched_param {
152 #[cfg(not(any(target_env = "musl", target_env = "ohos")))]
153 fn from(param: SchedulerParams) -> Self {
154 libc::sched_param {
155 sched_priority: param.priority,
156 }
157 }
158
159 #[cfg(any(target_env = "musl", target_env = "ohos"))]
160 fn from(param: SchedulerParams) -> Self {
161 let ts_zero = libc::timespec {
162 tv_sec: 0,
163 tv_nsec: 0,
164 };
165 libc::sched_param {
168 sched_priority: param.priority,
169 sched_ss_init_budget: ts_zero.clone(),
170 sched_ss_low_priority: 0,
171 sched_ss_repl_period: ts_zero.clone(),
172 sched_ss_max_repl: 0,
173 }
174 }
175}
176
177impl From<libc::sched_param> for SchedulerParams {
178 fn from(param: libc::sched_param) -> Self {
179 SchedulerParams {
180 priority: param.sched_priority,
181 }
182 }
183}
184
185pub trait IntoSchedParam {
186 fn into_sched_param(self) -> SchedulerParams;
187}
188
189impl IntoSchedParam for i32 {
190 fn into_sched_param(self) -> SchedulerParams {
191 SchedulerParams {
192 priority: self as c_int,
193 }
194 }
195}
196
197impl<T: IntoSchedParam> IntoSchedParam for Option<T> {
198 fn into_sched_param(self) -> SchedulerParams {
199 match self {
200 None => SchedulerParams { priority: 0 },
201 Some(param) => param.into_sched_param(),
202 }
203 }
204}
205
206#[cfg_attr(target_os = "macos", allow(unused))]
207#[derive(Debug, Clone)]
208pub struct ParameterizedScheduler {
209 scheduler: Scheduler,
210 params: SchedulerParams,
211}
212
213impl ParameterizedScheduler {
214 #[cfg_attr(target_os = "macos", allow(unused_variables))]
215 pub fn set_on(self, pid: Pid) -> RtResult<()> {
216 #[cfg(target_os = "linux")]
217 return set_scheduler(pid, self.scheduler, self.params);
218 #[cfg(target_os = "macos")]
219 return Err(PreemptRtError::NonLinuxPlatform("macos"));
220 }
221
222 pub fn set_current(self) -> RtResult<()> {
223 self.set_on(Pid::current_thread())
224 }
225}
226
227#[cfg(target_os = "linux")]
228mod linux {
229 use super::*;
230 use std::mem::MaybeUninit;
231
232 pub fn get_scheduler(pid: Pid) -> RtResult<Scheduler> {
235 let res = unsafe { libc::sched_getscheduler(pid.into()) };
236 handle_errno(res).and_then(Scheduler::try_from)
237 }
238
239 pub fn set_scheduler(pid: Pid, scheduler: Scheduler, param: SchedulerParams) -> RtResult<()> {
249 let param: libc::sched_param = param.into();
250 let res = unsafe { libc::sched_setscheduler(pid.into(), scheduler as c_int, ¶m) };
251
252 handle_errno(res).map(drop)
253 }
254
255 pub fn get_scheduler_params(pid: Pid) -> RtResult<SchedulerParams> {
258 let mut param: MaybeUninit<libc::sched_param> = MaybeUninit::uninit();
259 let res = unsafe { libc::sched_getparam(pid.into(), param.as_mut_ptr()) };
260
261 handle_errno(res).map(|_| unsafe { param.assume_init() }.into())
262 }
263 pub fn set_scheduler_params(pid: Pid, param: SchedulerParams) -> RtResult<()> {
269 let param: libc::sched_param = param.into();
270 let res = unsafe { libc::sched_setparam(pid.into(), ¶m) };
271 handle_errno(res).map(drop)
272 }
273}
274
275#[cfg(target_os = "linux")]
276pub use linux::*;