ostd/task/scheduler/
info.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Scheduling related information in a task.
4
5use core::sync::atomic::{AtomicU32, Ordering};
6
7use crate::{cpu::CpuId, task::Task};
8
9/// Fields of a task that OSTD will never touch.
10///
11/// The type ought to be defined by the OSTD user and injected into the task.
12/// They are not part of the dynamic task data because it's slower there for
13/// the user-defined scheduler to access. The better ways to let the user
14/// define them, such as
15/// [existential types](https://github.com/rust-lang/rfcs/pull/2492) do not
16/// exist yet. So we decide to define them in OSTD.
17#[derive(Debug)]
18pub struct TaskScheduleInfo {
19    /// The CPU that the task would like to be running on.
20    pub cpu: AtomicCpuId,
21}
22
23/// An atomic CPUID container.
24#[derive(Debug)]
25pub struct AtomicCpuId(AtomicU32);
26
27impl AtomicCpuId {
28    /// The null value of CPUID.
29    ///
30    /// An `AtomicCpuId` with `AtomicCpuId::NONE` as its inner value is empty.
31    const NONE: u32 = u32::MAX;
32
33    /// Sets the inner value of an `AtomicCpuId` if it's empty.
34    ///
35    /// The return value is a result indicating whether the new value was written
36    /// and containing the previous value. If the previous value is empty, it returns
37    /// `Ok(())`. Otherwise, it returns `Err(previous_value)` which the previous
38    /// value is a valid CPU ID.
39    pub fn set_if_is_none(&self, cpu_id: CpuId) -> core::result::Result<(), CpuId> {
40        self.0
41            .compare_exchange(
42                Self::NONE,
43                cpu_id.into(),
44                Ordering::Relaxed,
45                Ordering::Relaxed,
46            )
47            .map(|_| ())
48            .map_err(|prev| (prev as usize).try_into().unwrap())
49    }
50
51    /// Sets the inner value of an `AtomicCpuId` anyway.
52    pub fn set_anyway(&self, cpu_id: CpuId) {
53        self.0.store(cpu_id.into(), Ordering::Relaxed);
54    }
55
56    /// Sets the inner value of an `AtomicCpuId` to `AtomicCpuId::NONE`, i.e. makes
57    /// an `AtomicCpuId` empty.
58    pub fn set_to_none(&self) {
59        self.0.store(Self::NONE, Ordering::Relaxed);
60    }
61
62    /// Gets the inner value of an `AtomicCpuId`.
63    pub fn get(&self) -> Option<CpuId> {
64        let val = self.0.load(Ordering::Relaxed);
65        if val == Self::NONE {
66            None
67        } else {
68            Some((val as usize).try_into().ok()?)
69        }
70    }
71}
72
73impl Default for AtomicCpuId {
74    fn default() -> Self {
75        Self(AtomicU32::new(Self::NONE))
76    }
77}
78
79impl CommonSchedInfo for Task {
80    fn cpu(&self) -> &AtomicCpuId {
81        &self.schedule_info().cpu
82    }
83}
84
85/// Trait for fetching common scheduling information.
86pub trait CommonSchedInfo {
87    /// Gets the CPU that the task is running on or lately ran on.
88    fn cpu(&self) -> &AtomicCpuId;
89}