Skip to main content

osiris/sched/
thread.rs

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