osiris/sched/
thread.rs

1// ----------------------------------- Identifiers -----------------------------------
2
3use core::fmt::Display;
4use core::{borrow::Borrow, ffi::c_void};
5
6use hal::stack::{FinFn, Stacklike};
7use hal::{Stack, stack::EntryFn};
8use proc_macros::TaggedLinks;
9
10use crate::error::Result;
11use crate::sched::task::{self, KERNEL_TASK};
12use crate::types::list;
13use crate::types::{
14    rbtree::{self, Compare},
15    traits::{Project, ToIndex},
16};
17use crate::uapi;
18
19pub const IDLE_THREAD: UId = UId {
20    uid: 0,
21    tid: Id {
22        id: 0,
23        owner: KERNEL_TASK,
24    },
25};
26
27/// Id of a task. This is only unique within a Task.
28#[proc_macros::fmt]
29#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
30pub struct Id {
31    id: usize,
32    owner: task::UId,
33}
34
35#[allow(dead_code)]
36impl Id {
37    pub fn new(id: usize, owner: task::UId) -> Self {
38        Self { id, owner }
39    }
40
41    pub fn as_usize(&self) -> usize {
42        self.id
43    }
44
45    pub fn owner(&self) -> task::UId {
46        self.owner
47    }
48
49    pub fn get_uid(&self, uid: usize) -> UId {
50        UId { uid, tid: *self }
51    }
52}
53
54/// Unique identifier for a thread. Build from TaskId and ThreadId.
55#[proc_macros::fmt]
56#[derive(Clone, Copy)]
57#[allow(dead_code)]
58pub struct UId {
59    /// A globally unique identifier for the thread.
60    uid: usize,
61    /// The task-local identifier for the thread.
62    tid: Id,
63}
64
65#[allow(dead_code)]
66impl UId {
67    pub fn new(uid: usize, tid: Id) -> Self {
68        Self { uid, tid }
69    }
70
71    pub fn tid(&self) -> Id {
72        self.tid
73    }
74
75    pub fn as_usize(&self) -> usize {
76        self.uid
77    }
78
79    pub fn owner(&self) -> task::UId {
80        self.tid.owner()
81    }
82}
83
84impl PartialEq for UId {
85    fn eq(&self, other: &Self) -> bool {
86        self.uid == other.uid
87    }
88}
89
90impl Eq for UId {}
91
92impl Into<usize> for UId {
93    fn into(self) -> usize {
94        self.uid
95    }
96}
97
98impl PartialOrd for UId {
99    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
100        Some(self.cmp(other))
101    }
102}
103
104impl Ord for UId {
105    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
106        self.uid.cmp(&other.uid)
107    }
108}
109
110impl ToIndex for UId {
111    fn to_index<Q: Borrow<Self>>(idx: Option<Q>) -> usize {
112        idx.as_ref().map_or(0, |k| k.borrow().uid)
113    }
114}
115
116impl Display for UId {
117    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        write!(f, "{}-{}", self.tid.owner(), self.tid.as_usize())
119    }
120}
121
122// -------------------------------------------------------------------------
123
124/// The state of a thread.
125#[proc_macros::fmt]
126#[derive(Clone, Copy, PartialEq, Eq)]
127#[allow(dead_code)]
128pub enum RunState {
129    /// The thread is currently using the cpu.
130    Runs,
131    /// The thread is ready to run, but is not running.
132    Ready,
133    /// The thread is waiting for an event/signal to unblock it.
134    Waits,
135}
136
137#[proc_macros::fmt]
138#[derive(Clone, Copy)]
139pub struct State {
140    run_state: RunState,
141    stack: Stack,
142}
143
144#[proc_macros::fmt]
145#[derive(Clone, Copy, TaggedLinks)]
146pub struct RtServer {
147    budget: u32,
148    budget_left: u32,
149    period: u32,
150    deadline: u64,
151
152    // Back-reference to the thread uid.
153    uid: UId,
154
155    /// Real-time tree links for the server.
156    #[rbtree(tag = RtTree, idx = UId)]
157    _rt_links: rbtree::Links<RtTree, UId>,
158}
159
160impl RtServer {
161    pub fn new(budget: u32, period: u32, deadline: u64, uid: UId) -> Self {
162        Self {
163            budget,
164            budget_left: budget,
165            period,
166            deadline,
167            uid,
168            _rt_links: rbtree::Links::new(),
169        }
170    }
171
172    #[allow(dead_code)]
173    pub fn budget_left(&self) -> u32 {
174        self.budget_left
175    }
176
177    pub fn budget(&self) -> u32 {
178        self.budget
179    }
180
181    fn violates_sched(&self, now: u64) -> bool {
182        self.budget_left as u64 * self.period as u64
183            > self.budget as u64 * (self.deadline.saturating_sub(now))
184    }
185
186    pub fn on_wakeup(&mut self, now: u64) {
187        if self.deadline <= now || self.violates_sched(now) {
188            self.deadline = now + self.period as u64;
189            self.budget_left = self.budget;
190        }
191    }
192
193    #[allow(dead_code)]
194    pub fn replenish(&mut self) {
195        self.deadline = self.deadline + self.period as u64;
196        self.budget_left += self.budget;
197    }
198
199    pub fn consume(&mut self, dt: u64) -> Option<u64> {
200        self.budget_left = self.budget_left.saturating_sub(dt as u32);
201
202        if self.budget_left == 0 {
203            return Some(self.deadline);
204        }
205
206        None
207    }
208
209    #[allow(dead_code)]
210    pub fn deadline(&self) -> u64 {
211        self.deadline
212    }
213
214    #[allow(dead_code)]
215    pub fn uid(&self) -> UId {
216        self.uid
217    }
218}
219
220impl Compare<RtTree, UId> for RtServer {
221    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
222        let ord = self.deadline.cmp(&other.deadline);
223
224        if ord == core::cmp::Ordering::Equal {
225            self.uid.cmp(&other.uid)
226        } else {
227            ord
228        }
229    }
230}
231
232#[proc_macros::fmt]
233#[derive(Clone, Copy, TaggedLinks)]
234pub struct Waiter {
235    /// The time when the Thread will be awakened.
236    until: u64,
237
238    // Back-reference to the thread uid.
239    uid: UId,
240    /// Wakup tree links for the thread.
241    #[rbtree(tag = WakupTree, idx = UId)]
242    _wakeup_links: rbtree::Links<WakupTree, UId>,
243}
244
245impl Waiter {
246    pub fn new(until: u64, uid: UId) -> Self {
247        Self {
248            until,
249            uid,
250            _wakeup_links: rbtree::Links::new(),
251        }
252    }
253
254    pub fn until(&self) -> u64 {
255        self.until
256    }
257
258    #[allow(dead_code)]
259    pub fn set_until(&mut self, until: u64) {
260        self.until = until;
261    }
262}
263
264impl Compare<WakupTree, UId> for Waiter {
265    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
266        match self.until.cmp(&other.until) {
267            core::cmp::Ordering::Equal => self.uid.cmp(&other.uid),
268            ord => ord,
269        }
270    }
271}
272
273#[proc_macros::fmt]
274#[derive(Clone, Copy)]
275pub struct WakupTree;
276#[proc_macros::fmt]
277#[derive(Clone, Copy)]
278pub struct RtTree;
279
280#[proc_macros::fmt]
281#[derive(Clone, Copy)]
282pub struct RRList;
283
284#[proc_macros::fmt]
285#[derive(Clone, Copy)]
286pub struct ThreadList;
287
288pub struct Attributes {
289    pub entry: EntryFn,
290    pub fin: Option<FinFn>,
291    pub attrs: Option<uapi::sched::RtAttrs>,
292}
293
294/// The struct representing a thread.
295#[proc_macros::fmt]
296#[derive(Clone, Copy, TaggedLinks)]
297pub struct Thread {
298    /// The current state of the thread.
299    state: State,
300    /// The unique identifier of the thread.
301    uid: UId,
302    /// If the thread is real-time, its contains a constant bandwidth server.
303    rt_server: Option<RtServer>,
304
305    waiter: Option<Waiter>,
306
307    #[list(tag = RRList, idx = UId)]
308    rr_links: list::Links<RRList, UId>,
309
310    #[list(tag = ThreadList, idx = UId)]
311    thread_links: list::Links<ThreadList, UId>,
312}
313
314#[allow(dead_code)]
315impl Thread {
316    /// Create a new thread.
317    ///
318    /// `stack` - The stack of the thread.
319    ///
320    /// Returns a new thread.
321    pub fn new(uid: UId, stack: Stack, rtattrs: Option<uapi::sched::RtAttrs>) -> Self {
322        let server =
323            rtattrs.map(|attrs| RtServer::new(attrs.budget, attrs.period, attrs.deadline, uid));
324        Self {
325            state: State {
326                run_state: RunState::Ready,
327                stack,
328            },
329            uid,
330            rt_server: server,
331            waiter: None,
332            rr_links: list::Links::new(),
333            thread_links: list::Links::new(),
334        }
335    }
336
337    pub fn set_waiter(&mut self, waiter: Option<Waiter>) {
338        self.waiter = waiter;
339    }
340
341    pub fn waiter(&self) -> Option<&Waiter> {
342        self.waiter.as_ref()
343    }
344
345    pub fn save_ctx(&mut self, ctx: *mut c_void) -> Result<()> {
346        let sp = self.state.stack.create_sp(ctx)?;
347        self.state.stack.set_sp(sp);
348        Ok(())
349    }
350
351    pub fn set_run_state(&mut self, state: RunState) {
352        self.state.run_state = state;
353    }
354
355    pub fn rt_server(&self) -> Option<&RtServer> {
356        self.rt_server.as_ref()
357    }
358
359    pub fn ctx(&self) -> *mut c_void {
360        self.state.stack.sp()
361    }
362
363    pub fn uid(&self) -> UId {
364        self.uid
365    }
366
367    pub fn task_id(&self) -> task::UId {
368        self.uid.tid().owner()
369    }
370}
371
372impl PartialEq for Thread {
373    fn eq(&self, other: &Self) -> bool {
374        self.uid == other.uid
375    }
376}
377
378impl Project<RtServer> for Thread {
379    fn project(&self) -> Option<&RtServer> {
380        self.rt_server.as_ref()
381    }
382
383    fn project_mut(&mut self) -> Option<&mut RtServer> {
384        self.rt_server.as_mut()
385    }
386}
387
388impl Project<Waiter> for Thread {
389    fn project(&self) -> Option<&Waiter> {
390        self.waiter.as_ref()
391    }
392
393    fn project_mut(&mut self) -> Option<&mut Waiter> {
394        self.waiter.as_mut()
395    }
396}