Skip to main content

kernel/
process_array.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! Data structure for storing `Process`es.
6//!
7//! Many Tock boards store a fixed-length array of process control blocks
8//! (PCB) for easy management and traversal of running processes. The
9//! `ProcessArray` type facilitates this.
10//!
11//! The general type for the process array abstraction is
12//! `[&Process; NUM_PROCS]`. That is, the array is only sized to store
13//! references to each PCB. The actual PCB is allocated in the process's
14//! allocated memory.
15
16use crate::{capabilities, process};
17use core::cell::Cell;
18
19/// Represents a slot for a process in a [`ProcessArray`].
20///
21/// A slot can be empty (`None`), or hold a reference to a
22/// [`Process`](process::Process).
23///
24/// The `ProcessSlot` type is useful for allowing slices of processes without
25/// knowing the fixed number of processes, or being templated on `NUM_PROCS`.
26/// That is, interfaces can use `[ProcessSlot]` to just use an array of process
27/// slots.
28#[derive(Clone)]
29pub struct ProcessSlot {
30    /// Optionally points to a process.
31    pub(crate) proc: Cell<Option<&'static dyn process::Process>>,
32}
33
34impl ProcessSlot {
35    pub(crate) fn set(&self, process: &'static dyn process::Process) {
36        self.proc.set(Some(process));
37    }
38
39    /// Install a [`Process`](process::Process) into this process slot from outside the
40    /// kernel crate.
41    ///
42    /// The normal `set()` method is `pub(crate)` because, in
43    /// production, only the kernel's own process-loading machinery should be
44    /// able to populate a process slot. However, test code and external process
45    /// implementations live in separate crates and still need a way to register
46    /// a process with the kernel.
47    ///
48    /// `set_external` provides that path: it is **public** but requires the
49    /// caller to present an
50    /// [`ExternalProcessCapability`](capabilities::ExternalProcessCapability).
51    /// This capability is an *unsafe-to-implement* marker trait, so only
52    /// explicitly trusted code (typically a test harness) can obtain it. The
53    /// capability prevents arbitrary crates from silently replacing a running
54    /// process.
55    pub fn set_external(
56        &self,
57        process: &'static dyn process::Process,
58        _capability: &dyn capabilities::ExternalProcessCapability,
59    ) {
60        self.proc.set(Some(process));
61    }
62
63    /// Return the underlying [`process::Process`] if the slot contains a
64    /// process.
65    pub fn get(&self) -> Option<&'static dyn process::Process> {
66        self.proc.get()
67    }
68
69    /// Check if the slot contains a process with a matching process ID.
70    pub fn contains_process_with_id(&self, identifier: usize) -> bool {
71        match self.proc.get() {
72            Some(process) => process.processid().id() == identifier,
73            None => false,
74        }
75    }
76}
77
78/// Storage for a fixed-size array of `Process`es.
79pub struct ProcessArray<const NUM_PROCS: usize> {
80    processes: [ProcessSlot; NUM_PROCS],
81}
82
83impl<const NUM_PROCS: usize> ProcessArray<NUM_PROCS> {
84    pub const fn new() -> Self {
85        Self {
86            processes: [const {
87                ProcessSlot {
88                    proc: Cell::new(None),
89                }
90            }; NUM_PROCS],
91        }
92    }
93
94    pub fn as_slice(&self) -> &[ProcessSlot] {
95        &self.processes
96    }
97}
98
99impl<const NUM_PROCS: usize> core::ops::Index<usize> for ProcessArray<NUM_PROCS> {
100    type Output = ProcessSlot;
101
102    fn index(&self, i: usize) -> &ProcessSlot {
103        &self.processes[i]
104    }
105}