1use std::collections::HashMap;
13use std::sync::atomic::{AtomicU64, Ordering};
14
15use super::error::{LifecycleError, RuntimeError};
16use super::process::FlowId;
17use super::sync_lock;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub struct MonitorRef(pub(crate) u64);
25
26static NEXT_MONITOR: AtomicU64 = AtomicU64::new(1);
27
28impl MonitorRef {
29 #[inline]
30 pub fn as_u64(self) -> u64 {
31 self.0
32 }
33
34 #[inline]
35 pub(crate) fn from_u64(raw: u64) -> Self {
36 Self(raw)
37 }
38}
39
40impl std::fmt::Display for MonitorRef {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "monitor#{}", self.0)
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[repr(u8)]
52pub enum FlowExitReason {
53 Normal = 0,
54 Shutdown = 1,
56 Killed = 2,
57 Fault = 3,
58 MailboxOverflow = 4,
60 Supervisor = 5,
61 Link = 6,
62}
63
64impl FlowExitReason {
65 pub fn from_u64(raw: u64) -> Option<Self> {
66 Some(match raw {
67 0 => Self::Normal,
68 1 => Self::Shutdown,
69 2 => Self::Killed,
70 3 => Self::Fault,
71 4 => Self::MailboxOverflow,
72 5 => Self::Supervisor,
73 6 => Self::Link,
74 _ => return None,
75 })
76 }
77
78 #[inline]
79 pub fn as_u64(self) -> u64 {
80 self as u64
81 }
82
83 #[inline]
89 pub fn is_abnormal(self) -> bool {
90 !matches!(self, Self::Normal)
91 }
92}
93
94impl std::fmt::Display for FlowExitReason {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 let name = match self {
97 Self::Normal => "normal",
98 Self::Shutdown => "shutdown",
99 Self::Killed => "killed",
100 Self::Fault => "fault",
101 Self::MailboxOverflow => "mailbox-overflow",
102 Self::Supervisor => "supervisor",
103 Self::Link => "link",
104 };
105 f.write_str(name)
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub struct DownEvent {
112 pub monitor: MonitorRef,
113 pub owner: FlowId,
114 pub target: FlowId,
115 pub reason: FlowExitReason,
116}
117
118#[derive(Debug, Clone, Copy)]
119struct MonitorEntry {
120 owner: FlowId,
121 target: FlowId,
122}
123
124pub struct MonitorTable {
126 monitors: HashMap<MonitorRef, MonitorEntry>,
127}
128
129impl MonitorTable {
130 pub fn new() -> Self {
131 Self {
132 monitors: HashMap::new(),
133 }
134 }
135
136 pub fn create(&mut self, owner: FlowId, target: FlowId) -> MonitorRef {
137 let monitor = MonitorRef(NEXT_MONITOR.fetch_add(1, Ordering::Relaxed));
138 self.monitors.insert(
139 monitor,
140 MonitorEntry { owner, target },
141 );
142 monitor
143 }
144
145 pub fn remove_owned(&mut self, owner: FlowId, monitor: MonitorRef) -> Result<(), LifecycleError> {
147 match self.monitors.get(&monitor) {
148 Some(entry) if entry.owner == owner => {
149 self.monitors.remove(&monitor);
150 Ok(())
151 }
152 Some(_) => Err(LifecycleError::NotOwner),
153 None => Err(LifecycleError::InvalidMonitor),
154 }
155 }
156
157 pub fn remove_owned_by(&mut self, owner: FlowId) {
159 self.monitors.retain(|_, entry| entry.owner != owner);
160 }
161
162 pub fn notify_target_exit(
164 &mut self,
165 target: FlowId,
166 reason: FlowExitReason,
167 ) -> Vec<DownEvent> {
168 let mut events = Vec::new();
169 self.monitors.retain(|monitor, entry| {
170 if entry.target != target {
171 return true;
172 }
173 events.push(DownEvent {
174 monitor: *monitor,
175 owner: entry.owner,
176 target,
177 reason,
178 });
179 false
180 });
181 events.sort_unstable_by_key(|event| event.monitor.0);
182 events
183 }
184}
185
186impl Default for MonitorTable {
187 fn default() -> Self {
188 Self::new()
189 }
190}
191
192pub struct MonitorStore {
194 inner: std::sync::Mutex<MonitorTable>,
195}
196
197impl MonitorStore {
198 pub fn new() -> Self {
199 Self {
200 inner: std::sync::Mutex::new(MonitorTable::new()),
201 }
202 }
203
204 pub fn create(&self, owner: FlowId, target: FlowId) -> Result<MonitorRef, RuntimeError> {
205 Ok(sync_lock::lock(&self.inner, "MonitorStore::create")?.create(owner, target))
206 }
207
208 pub fn remove_owned(
209 &self,
210 owner: FlowId,
211 monitor: MonitorRef,
212 ) -> Result<Result<(), LifecycleError>, RuntimeError> {
213 Ok(sync_lock::lock(&self.inner, "MonitorStore::remove_owned")?.remove_owned(owner, monitor))
214 }
215
216 pub fn remove_owned_by(&self, owner: FlowId) -> Result<(), RuntimeError> {
217 sync_lock::lock(&self.inner, "MonitorStore::remove_owned_by")?.remove_owned_by(owner);
218 Ok(())
219 }
220
221 pub fn notify_target_exit(
222 &self,
223 target: FlowId,
224 reason: FlowExitReason,
225 ) -> Result<Vec<DownEvent>, RuntimeError> {
226 Ok(sync_lock::lock(&self.inner, "MonitorStore::notify_target_exit")?
227 .notify_target_exit(target, reason))
228 }
229}
230
231impl Default for MonitorStore {
232 fn default() -> Self {
233 Self::new()
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::scheduler::process::next_flow_id;
241
242 #[test]
243 fn notify_removes_and_sorts() {
244 let mut table = MonitorTable::new();
245 let owner = next_flow_id();
246 let target = next_flow_id();
247 let a = table.create(owner, target);
248 let b = table.create(owner, target);
249 let events = table.notify_target_exit(target, FlowExitReason::Fault);
250 assert_eq!(events.len(), 2);
251 assert_eq!(events[0].monitor, a);
252 assert_eq!(events[1].monitor, b);
253 assert!(table.notify_target_exit(target, FlowExitReason::Fault).is_empty());
254 }
255
256 #[test]
257 fn remove_owned_rejects_other_flow() {
258 let mut table = MonitorTable::new();
259 let owner = next_flow_id();
260 let other = next_flow_id();
261 let target = next_flow_id();
262 let mon = table.create(owner, target);
263 assert_eq!(table.remove_owned(other, mon), Err(LifecycleError::NotOwner));
264 assert!(table.remove_owned(owner, mon).is_ok());
265 }
266}