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}