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 {
12 #[error("c function returned errno: {0}")]
13 Errno(c_int),
14 #[error("unknown scheduler for value {0}")]
15 UnknownScheduler(c_int),
16 #[error("priority {0} is higher than max priority {1}")]
17 PriorityAboveMax(c_int, c_int),
18 #[error("priority {0} is lower than min priority {1}")]
19 PriorityBelowMin(c_int, c_int),
20 #[cfg(feature = "non-linux-stubs")]
21 #[error("current platform {0} does not support preempt-rt")]
22 NonLinuxPlatform(&'static str),
23}
24
25#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
26pub struct Pid(pid_t);
28
29impl Pid {
30 pub const fn current_thread() -> Self {
34 Pid(0)
35 }
36}
37
38impl From<Pid> for pid_t {
39 fn from(pid: Pid) -> Self {
40 pid.0
41 }
42}
43
44impl fmt::Display for Pid {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 fmt::Display::fmt(&self.0, f)
47 }
48}
49
50#[repr(i32)]
51#[allow(non_camel_case_types)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
53pub enum Scheduler {
60 #[cfg(target_os = "linux")]
62 SCHED_NORMAL = libc::SCHED_NORMAL,
63 #[cfg(target_os = "macos")]
65 SCHED_NORMAL = libc::SCHED_OTHER,
66 SCHED_FIFO = libc::SCHED_FIFO,
70 SCHED_RR = libc::SCHED_RR,
72 #[cfg(target_os = "linux")]
76 SCHED_BATCH = libc::SCHED_BATCH,
77 #[cfg(target_os = "linux")]
80 SCHED_IDLE = libc::SCHED_IDLE,
81 #[cfg(target_os = "linux")]
85 SCHED_DEADLINE = libc::SCHED_DEADLINE,
86}
87
88impl TryFrom<c_int> for Scheduler {
89 type Error = PreemptRtError;
90
91 fn try_from(value: c_int) -> RtResult<Self> {
92 match value {
93 #[cfg(target_os = "linux")]
94 libc::SCHED_NORMAL => Ok(Scheduler::SCHED_NORMAL),
95 #[cfg(target_os = "macos")]
96 libc::SCHED_OTHER => Ok(Scheduler::SCHED_NORMAL),
97 libc::SCHED_FIFO => Ok(Scheduler::SCHED_FIFO),
98 libc::SCHED_RR => Ok(Scheduler::SCHED_RR),
99 #[cfg(target_os = "linux")]
100 libc::SCHED_BATCH => Ok(Scheduler::SCHED_BATCH),
101 #[cfg(target_os = "linux")]
102 libc::SCHED_IDLE => Ok(Scheduler::SCHED_IDLE),
103 #[cfg(target_os = "linux")]
104 libc::SCHED_DEADLINE => Ok(Scheduler::SCHED_DEADLINE),
105 _ => Err(PreemptRtError::UnknownScheduler(value)),
106 }
107 }
108}
109
110fn handle_errno(result: c_int) -> RtResult<c_int> {
111 if result == -1 {
112 #[cfg(target_os = "linux")]
113 return Err(PreemptRtError::Errno(unsafe { *libc::__errno_location() }));
114 #[cfg(target_os = "macos")]
115 return Err(PreemptRtError::Errno(unsafe { *libc::__error() }));
116 } else {
117 Ok(result)
118 }
119}
120
121impl Scheduler {
122 pub fn priority_max(&self) -> RtResult<c_int> {
124 let res = unsafe { libc::sched_get_priority_max(*self as c_int) };
125 handle_errno(res)
126 }
127
128 pub fn priority_min(&self) -> RtResult<c_int> {
130 let res = unsafe { libc::sched_get_priority_min(*self as c_int) };
131 handle_errno(res)
132 }
133
134 pub fn with_params(self, params: SchedulerParams) -> RtResult<ParameterizedScheduler> {
137 let priority = params.priority;
138 let max = self.priority_max()?;
139 let min = self.priority_min()?;
140 if priority > max {
141 Err(PriorityAboveMax(priority, max))
142 } else if priority < min {
143 Err(PriorityBelowMin(priority, min))
144 } else {
145 Ok(ParameterizedScheduler {
146 scheduler: self,
147 params,
148 })
149 }
150 }
151}
152
153#[repr(C)]
154#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
155pub struct SchedulerParams {
158 pub priority: c_int,
160}
161
162#[cfg(target_os = "linux")]
163impl From<SchedulerParams> for libc::sched_param {
164 #[cfg(not(any(target_env = "musl", target_env = "ohos")))]
165 fn from(param: SchedulerParams) -> Self {
166 libc::sched_param {
167 sched_priority: param.priority,
168 }
169 }
170
171 #[cfg(any(target_env = "musl", target_env = "ohos"))]
172 fn from(param: SchedulerParams) -> Self {
173 let ts_zero = libc::timespec {
174 tv_sec: 0,
175 tv_nsec: 0,
176 };
177 libc::sched_param {
180 sched_priority: param.priority,
181 sched_ss_init_budget: ts_zero.clone(),
182 sched_ss_low_priority: 0,
183 sched_ss_repl_period: ts_zero.clone(),
184 sched_ss_max_repl: 0,
185 }
186 }
187}
188
189impl From<libc::sched_param> for SchedulerParams {
190 fn from(param: libc::sched_param) -> Self {
191 SchedulerParams {
192 priority: param.sched_priority,
193 }
194 }
195}
196
197pub trait IntoSchedParams {
198 fn into_sched_params(self) -> SchedulerParams;
199}
200
201impl IntoSchedParams for SchedulerParams {
202 fn into_sched_params(self) -> SchedulerParams {
203 self
204 }
205}
206
207impl IntoSchedParams for i32 {
208 fn into_sched_params(self) -> SchedulerParams {
209 SchedulerParams {
210 priority: self as c_int,
211 }
212 }
213}
214
215impl<T: IntoSchedParams> IntoSchedParams for Option<T> {
216 fn into_sched_params(self) -> SchedulerParams {
217 match self {
218 None => SchedulerParams { priority: 0 },
219 Some(param) => param.into_sched_params(),
220 }
221 }
222}
223
224#[cfg_attr(target_os = "macos", allow(unused))]
225#[derive(Debug, Clone)]
226pub struct ParameterizedScheduler {
227 scheduler: Scheduler,
228 params: SchedulerParams,
229}
230
231impl ParameterizedScheduler {
232 #[cfg_attr(target_os = "macos", allow(unused_variables))]
233 pub fn set_on(self, pid: Pid) -> RtResult<()> {
234 #[cfg(target_os = "linux")]
235 return set_scheduler(pid, self.scheduler, self.params);
236 #[cfg(target_os = "macos")]
237 return Err(PreemptRtError::NonLinuxPlatform("macos"));
238 }
239
240 pub fn set_current(self) -> RtResult<()> {
241 self.set_on(Pid::current_thread())
242 }
243}
244
245#[cfg(target_os = "linux")]
246mod linux {
247 use super::*;
248 use std::mem::MaybeUninit;
249
250 pub fn get_scheduler(pid: Pid) -> RtResult<Scheduler> {
253 let res = unsafe { libc::sched_getscheduler(pid.into()) };
254 handle_errno(res).and_then(Scheduler::try_from)
255 }
256
257 pub fn set_scheduler(pid: Pid, scheduler: Scheduler, param: SchedulerParams) -> RtResult<()> {
267 let param: libc::sched_param = param.into();
268 let res = unsafe { libc::sched_setscheduler(pid.into(), scheduler as c_int, ¶m) };
269
270 handle_errno(res).map(drop)
271 }
272
273 pub fn get_scheduler_params(pid: Pid) -> RtResult<SchedulerParams> {
276 let mut param: MaybeUninit<libc::sched_param> = MaybeUninit::uninit();
277 let res = unsafe { libc::sched_getparam(pid.into(), param.as_mut_ptr()) };
278
279 handle_errno(res).map(|_| unsafe { param.assume_init() }.into())
280 }
281 pub fn set_scheduler_params(pid: Pid, param: SchedulerParams) -> RtResult<()> {
287 let param: libc::sched_param = param.into();
288 let res = unsafe { libc::sched_setparam(pid.into(), ¶m) };
289 handle_errno(res).map(drop)
290 }
291}
292
293#[cfg(target_os = "linux")]
294pub use linux::*;