Skip to main content

embassy_supervisor/
graph_ref.rs

1use crate::TaskNode;
2
3#[cfg(feature = "trace-self")]
4static SELF_NODE_CFG: crate::NodeCfg = crate::NodeCfg::new("supervisor", crate::Mode::Pause, None);
5
6/// A reference to a registered graph, used for runtime introspection.
7pub struct GraphRef {
8    nodes: &'static [Option<&'static TaskNode>],
9    #[cfg(feature = "trace")]
10    chain: Chain,
11    #[cfg(feature = "trace-self")]
12    self_node: Option<TaskNode>,
13}
14
15impl GraphRef {
16    /// Build a graph reference from the fixed node slot array.
17    pub const fn new(nodes: &'static [Option<&'static TaskNode>]) -> Self {
18        Self {
19            nodes,
20            #[cfg(feature = "trace")]
21            chain: Chain::new(),
22            #[cfg(feature = "trace-self")]
23            self_node: Some(TaskNode::new(&SELF_NODE_CFG, false)),
24        }
25    }
26
27    /// Return the graph's node slot array.
28    pub const fn nodes(&self) -> &'static [Option<&'static TaskNode>] {
29        self.nodes
30    }
31
32    #[cfg(feature = "trace-self")]
33    /// Return the supervisor's own introspection node, if enabled.
34    pub const fn self_node(&'static self) -> Option<&'static TaskNode> {
35        self.self_node.as_ref()
36    }
37}
38
39#[cfg(feature = "data-deps")]
40pub(crate) static NO_GRAPH: GraphRef = GraphRef {
41    nodes: &[],
42    #[cfg(feature = "trace")]
43    chain: Chain::new(),
44    #[cfg(feature = "trace-self")]
45    self_node: None,
46};
47
48#[cfg(feature = "trace")]
49mod chain {
50    use core::cell::Cell;
51    use core::sync::atomic::Ordering;
52
53    use embassy_sync::blocking_mutex::Mutex;
54    use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
55    use portable_atomic::AtomicBool;
56
57    use super::GraphRef;
58
59    type Link = Mutex<CriticalSectionRawMutex, Cell<Option<&'static GraphRef>>>;
60
61    const fn unlinked() -> Link {
62        Mutex::new(Cell::new(None))
63    }
64
65    /// Per-graph link state.
66    pub(super) struct Chain {
67        next: Link,
68        /// Latched at registration so a graph started twice (a sub-graph
69        /// supervisor is legitimately `start()`/`teardown()`-cycled) is linked
70        /// once, without walking the chain to look for itself.
71        linked: AtomicBool,
72    }
73
74    impl Chain {
75        pub(super) const fn new() -> Self {
76            Self {
77                next: unlinked(),
78                linked: AtomicBool::new(false),
79            }
80        }
81    }
82
83    /// Head of the chain: the most recently registered graph.
84    static HEAD: Link = unlinked();
85
86    impl GraphRef {
87        /// Link this graph into the binary-wide chain, so the trace hooks can
88        /// resolve a task id to one of its nodes. Called by
89        /// [`Supervisor::start`](crate::Supervisor::start); idempotent, and
90        /// unbounded in the number of graphs.
91        pub fn register(&'static self) {
92            if self.chain.linked.swap(true, Ordering::AcqRel) {
93                return;
94            }
95            HEAD.lock(|head| {
96                self.chain.next.lock(|next| next.set(head.get()));
97                head.set(Some(self));
98            });
99        }
100    }
101
102    /// Return an iterator over every registered graph, most recent first.
103    pub fn graphs() -> Graphs {
104        Graphs {
105            next: HEAD.lock(Cell::get),
106        }
107    }
108
109    /// Iterator over registered graphs returned by [`graphs`](fn@graphs).
110    pub struct Graphs {
111        next: Option<&'static GraphRef>,
112    }
113
114    impl Iterator for Graphs {
115        type Item = &'static GraphRef;
116
117        fn next(&mut self) -> Option<Self::Item> {
118            let cur = self.next?;
119            self.next = cur.chain.next.lock(Cell::get);
120            Some(cur)
121        }
122    }
123}
124
125#[cfg(feature = "trace")]
126use chain::Chain;
127#[cfg(feature = "trace")]
128pub use chain::{Graphs, graphs};