1use crate::sched::PreemptRtError::{PriorityAboveMax, PriorityBelowMin};
2use libc::c_int;
3use thiserror::Error;
4
5pub type RtResult<T> = Result<T, PreemptRtError>;
7
8#[derive(Debug, Error)]
9pub 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#[cfg(not(target_os = "windows"))]
24#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
25pub struct Pid(libc::pid_t);
27
28#[cfg(target_os = "windows")]
29#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
30pub struct Pid(i32);
32
33impl Pid {
34 pub const fn current_thread() -> Self {
38 Pid(0)
39 }
40}
41
42#[cfg(not(target_os = "windows"))]
43impl From<Pid> for libc::pid_t {
44 fn from(pid: Pid) -> Self {
45 pid.0
46 }
47}
48
49#[cfg(not(target_os = "windows"))]
50impl std::fmt::Display for Pid {
51 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
52 std::fmt::Display::fmt(&self.0, f)
53 }
54}
55
56#[repr(i32)]
57#[allow(non_camel_case_types)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
59pub enum Scheduler {
66 #[cfg(target_os = "windows")]
67 SCHED_WINDOWS = 0,
68 #[cfg(target_os = "linux")]
70 SCHED_NORMAL = libc::SCHED_NORMAL,
71 #[cfg(target_os = "macos")]
73 SCHED_NORMAL = libc::SCHED_OTHER,
74 #[cfg(not(target_os = "windows"))]
78 SCHED_FIFO = libc::SCHED_FIFO,
79 #[cfg(target_os = "windows")]
80 SCHED_FIFO = 1,
81 #[cfg(not(target_os = "windows"))]
83 SCHED_RR = libc::SCHED_RR,
84 #[cfg(target_os = "linux")]
88 SCHED_BATCH = libc::SCHED_BATCH,
89 #[cfg(target_os = "linux")]
92 SCHED_IDLE = libc::SCHED_IDLE,
93 #[cfg(target_os = "linux")]
97 SCHED_DEADLINE = libc::SCHED_DEADLINE,
98}
99
100impl TryFrom<c_int> for Scheduler {
101 type Error = PreemptRtError;
102
103 fn try_from(value: c_int) -> RtResult<Self> {
104 match value {
105 #[cfg(target_os = "linux")]
106 libc::SCHED_NORMAL => Ok(Scheduler::SCHED_NORMAL),
107 #[cfg(target_os = "macos")]
108 libc::SCHED_OTHER => Ok(Scheduler::SCHED_NORMAL),
109 #[cfg(any(target_os = "linux", target_os = "macos"))]
110 libc::SCHED_FIFO => Ok(Scheduler::SCHED_FIFO),
111 #[cfg(any(target_os = "linux", target_os = "macos"))]
112 libc::SCHED_RR => Ok(Scheduler::SCHED_RR),
113 #[cfg(target_os = "linux")]
114 libc::SCHED_BATCH => Ok(Scheduler::SCHED_BATCH),
115 #[cfg(target_os = "linux")]
116 libc::SCHED_IDLE => Ok(Scheduler::SCHED_IDLE),
117 #[cfg(target_os = "linux")]
118 libc::SCHED_DEADLINE => Ok(Scheduler::SCHED_DEADLINE),
119 _ => Err(PreemptRtError::UnknownScheduler(value)),
120 }
121 }
122}
123
124#[cfg(not(target_os = "windows"))]
125fn handle_errno(result: c_int) -> RtResult<c_int> {
126 if result == -1 {
127 #[cfg(target_os = "linux")]
128 return Err(PreemptRtError::Errno(unsafe { *libc::__errno_location() }));
129 #[cfg(target_os = "macos")]
130 return Err(PreemptRtError::Errno(unsafe { *libc::__error() }));
131 } else {
132 Ok(result)
133 }
134}
135
136#[cfg(not(target_os = "windows"))]
137impl Scheduler {
138 pub fn priority_max(&self) -> RtResult<c_int> {
140 let res = unsafe { libc::sched_get_priority_max(*self as c_int) };
141 handle_errno(res)
142 }
143
144 pub fn priority_min(&self) -> RtResult<c_int> {
146 let res = unsafe { libc::sched_get_priority_min(*self as c_int) };
147 handle_errno(res)
148 }
149
150 pub fn with_params(self, params: SchedulerParams) -> ParameterizedScheduler {
152 ParameterizedScheduler {
153 scheduler: self,
154 params,
155 }
156 }
157}
158
159#[cfg(target_os = "windows")]
160impl Scheduler {
161 pub fn priority_max(&self) -> RtResult<c_int> {
165 Err(PreemptRtError::NonLinuxPlatform("windows"))
166 }
167
168 pub fn priority_min(&self) -> RtResult<c_int> {
172 Err(PreemptRtError::NonLinuxPlatform("windows"))
173 }
174
175 pub fn with_params(self, params: SchedulerParams) -> ParameterizedScheduler {
179 ParameterizedScheduler {
180 scheduler: Scheduler::SCHED_WINDOWS,
181 params,
182 }
183 }
184}
185
186#[repr(C)]
187#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
188pub struct SchedulerParams {
191 pub priority: c_int,
193}
194
195#[cfg(target_os = "linux")]
196impl From<SchedulerParams> for libc::sched_param {
197 #[cfg(not(any(target_env = "musl", target_env = "ohos")))]
198 fn from(param: SchedulerParams) -> Self {
199 libc::sched_param {
200 sched_priority: param.priority,
201 }
202 }
203
204 #[cfg(any(target_env = "musl", target_env = "ohos"))]
205 fn from(param: SchedulerParams) -> Self {
206 let ts_zero = libc::timespec {
207 tv_sec: 0,
208 tv_nsec: 0,
209 };
210 libc::sched_param {
213 sched_priority: param.priority,
214 sched_ss_init_budget: ts_zero.clone(),
215 sched_ss_low_priority: 0,
216 sched_ss_repl_period: ts_zero.clone(),
217 sched_ss_max_repl: 0,
218 }
219 }
220}
221
222#[cfg(any(target_os = "macos", target_os = "linux"))]
223impl From<libc::sched_param> for SchedulerParams {
224 fn from(param: libc::sched_param) -> Self {
225 SchedulerParams {
226 priority: param.sched_priority,
227 }
228 }
229}
230
231pub trait IntoSchedParams {
232 fn into_sched_params(self) -> SchedulerParams;
233}
234
235impl IntoSchedParams for SchedulerParams {
236 fn into_sched_params(self) -> SchedulerParams {
237 self
238 }
239}
240
241impl IntoSchedParams for i32 {
242 fn into_sched_params(self) -> SchedulerParams {
243 SchedulerParams {
244 priority: self as c_int,
245 }
246 }
247}
248
249impl<T: IntoSchedParams> IntoSchedParams for Option<T> {
250 fn into_sched_params(self) -> SchedulerParams {
251 match self {
252 None => SchedulerParams { priority: 0 },
253 Some(param) => param.into_sched_params(),
254 }
255 }
256}
257
258#[cfg_attr(any(target_os = "macos", target_os = "windows"), allow(unused))]
259#[derive(Debug, Clone)]
260pub struct ParameterizedScheduler {
261 scheduler: Scheduler,
262 params: SchedulerParams,
263}
264
265impl ParameterizedScheduler {
266 #[cfg_attr(
269 any(target_os = "macos", target_os = "windows"),
270 allow(unused_variables)
271 )]
272 pub fn set_on(self, pid: Pid) -> RtResult<()> {
273 let priority = self.params.priority;
274 let max = self.scheduler.priority_max()?;
275 let min = self.scheduler.priority_min()?;
276 if priority > max {
277 Err(PriorityAboveMax(priority, max))
278 } else if priority < min {
279 Err(PriorityBelowMin(priority, min))
280 } else {
281 #[cfg(target_os = "linux")]
282 return set_scheduler(pid, self.scheduler, self.params);
283 #[cfg(target_os = "macos")]
284 return Err(PreemptRtError::NonLinuxPlatform("macos"));
285 #[cfg(target_os = "windows")]
286 return Err(PreemptRtError::NonLinuxPlatform("windows"));
287 }
288 }
289
290 pub fn set_current(self) -> RtResult<()> {
291 self.set_on(Pid::current_thread())
292 }
293}
294
295#[cfg(target_os = "linux")]
296mod linux {
297 use super::*;
298 use std::mem::MaybeUninit;
299
300 pub fn get_scheduler(pid: Pid) -> RtResult<Scheduler> {
303 let res = unsafe { libc::sched_getscheduler(pid.into()) };
304 handle_errno(res).and_then(Scheduler::try_from)
305 }
306
307 pub fn set_scheduler(pid: Pid, scheduler: Scheduler, param: SchedulerParams) -> RtResult<()> {
317 let param: libc::sched_param = param.into();
318 let res = unsafe { libc::sched_setscheduler(pid.into(), scheduler as c_int, ¶m) };
319
320 handle_errno(res).map(drop)
321 }
322
323 pub fn get_scheduler_params(pid: Pid) -> RtResult<SchedulerParams> {
326 let mut param: MaybeUninit<libc::sched_param> = MaybeUninit::uninit();
327 let res = unsafe { libc::sched_getparam(pid.into(), param.as_mut_ptr()) };
328
329 handle_errno(res).map(|_| unsafe { param.assume_init() }.into())
330 }
331 pub fn set_scheduler_params(pid: Pid, param: SchedulerParams) -> RtResult<()> {
337 let param: libc::sched_param = param.into();
338 let res = unsafe { libc::sched_setparam(pid.into(), ¶m) };
339 handle_errno(res).map(drop)
340 }
341}
342
343#[cfg(target_os = "linux")]
344pub use linux::*;