1use std::collections::VecDeque;
4use std::num::NonZeroUsize;
5use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
6use std::sync::{Arc, Condvar, Mutex, Weak};
7use std::time::Duration;
8
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use crate::capability::CapabilityDenied;
13use crate::component::ComponentId;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
17pub enum LifecycleState {
18 Registered,
20 Starting,
22 Running,
24 Stopping,
26 Stopped,
28 Failed,
30 Removed,
32}
33
34#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
36pub struct LifecycleEvent {
37 pub sequence: u64,
39 pub component_id: ComponentId,
41 pub kind: LifecycleEventKind,
43}
44
45#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
47pub enum LifecycleEventKind {
48 Transition {
50 from: Option<LifecycleState>,
52 to: LifecycleState,
54 },
55 CapabilityDenied(CapabilityDenied),
57}
58
59pub struct LifecycleSubscription {
61 queue: Arc<SubscriberQueue>,
62}
63
64impl LifecycleSubscription {
65 pub fn recv_timeout(&self, timeout: Duration) -> Result<LifecycleEvent, EventReceiveError> {
71 let guard = self
72 .queue
73 .state
74 .lock()
75 .map_err(|_| EventReceiveError::Poisoned)?;
76 let (mut guard, _wait) = self
77 .queue
78 .ready
79 .wait_timeout_while(guard, timeout, |state| {
80 state.events.is_empty() && !state.closed
81 })
82 .map_err(|_| EventReceiveError::Poisoned)?;
83 if let Some(event) = guard.events.pop_front() {
84 return Ok(event);
85 }
86 if guard.closed {
87 Err(EventReceiveError::Closed)
88 } else {
89 Err(EventReceiveError::Timeout)
90 }
91 }
92
93 pub fn try_recv(&self) -> Result<LifecycleEvent, EventTryReceiveError> {
99 let mut state = self
100 .queue
101 .state
102 .lock()
103 .map_err(|_| EventTryReceiveError::Poisoned)?;
104 if let Some(event) = state.events.pop_front() {
105 Ok(event)
106 } else if state.closed {
107 Err(EventTryReceiveError::Closed)
108 } else {
109 Err(EventTryReceiveError::Empty)
110 }
111 }
112
113 #[must_use]
115 pub fn lagged_events(&self) -> usize {
116 self.queue.lagged.load(Ordering::Acquire)
117 }
118}
119
120impl Drop for LifecycleSubscription {
121 fn drop(&mut self) {
122 if let Ok(mut state) = self.queue.state.lock() {
123 state.closed = true;
124 self.queue.ready.notify_all();
125 }
126 }
127}
128
129#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
131pub enum EventReceiveError {
132 #[error("lifecycle event receive timed out")]
134 Timeout,
135 #[error("lifecycle event subscription is closed")]
137 Closed,
138 #[error("lifecycle event subscription synchronization is poisoned")]
140 Poisoned,
141}
142
143#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
145pub enum EventTryReceiveError {
146 #[error("lifecycle event subscription is empty")]
148 Empty,
149 #[error("lifecycle event subscription is closed")]
151 Closed,
152 #[error("lifecycle event subscription synchronization is poisoned")]
154 Poisoned,
155}
156
157#[derive(Default)]
158pub(crate) struct EventHub {
159 next_sequence: AtomicU64,
160 subscribers: Mutex<Vec<Weak<SubscriberQueue>>>,
161}
162
163impl EventHub {
164 pub(crate) fn subscribe(
165 &self,
166 capacity: NonZeroUsize,
167 ) -> Result<LifecycleSubscription, EventPublishError> {
168 let queue = Arc::new(SubscriberQueue {
169 capacity: capacity.get(),
170 state: Mutex::new(QueueState::default()),
171 ready: Condvar::new(),
172 lagged: AtomicUsize::new(0),
173 });
174 self.subscribers
175 .lock()
176 .map_err(|_| EventPublishError::Poisoned)?
177 .push(Arc::downgrade(&queue));
178 Ok(LifecycleSubscription { queue })
179 }
180
181 pub(crate) fn publish_transition(
182 &self,
183 component_id: ComponentId,
184 from: Option<LifecycleState>,
185 to: LifecycleState,
186 ) -> Result<(), EventPublishError> {
187 self.publish(component_id, LifecycleEventKind::Transition { from, to })
188 }
189
190 pub(crate) fn publish_denial(&self, denial: CapabilityDenied) -> Result<(), EventPublishError> {
191 self.publish(
192 denial.component_id,
193 LifecycleEventKind::CapabilityDenied(denial),
194 )
195 }
196
197 fn publish(
198 &self,
199 component_id: ComponentId,
200 kind: LifecycleEventKind,
201 ) -> Result<(), EventPublishError> {
202 let mut subscribers = self
203 .subscribers
204 .lock()
205 .map_err(|_| EventPublishError::Poisoned)?;
206 subscribers.retain(|subscriber| subscriber.strong_count() > 0);
207 if subscribers.is_empty() {
208 return Ok(());
209 }
210 let event = LifecycleEvent {
211 sequence: self.next_sequence.fetch_add(1, Ordering::AcqRel),
212 component_id,
213 kind,
214 };
215 for subscriber in subscribers.iter().filter_map(Weak::upgrade) {
216 let mut state = subscriber
217 .state
218 .lock()
219 .map_err(|_| EventPublishError::Poisoned)?;
220 if state.events.len() == subscriber.capacity {
221 let _discarded = state.events.pop_front();
222 subscriber.lagged.fetch_add(1, Ordering::AcqRel);
223 }
224 state.events.push_back(event.clone());
225 subscriber.ready.notify_one();
226 }
227 Ok(())
228 }
229}
230
231#[derive(Debug, Error)]
232pub(crate) enum EventPublishError {
233 #[error("lifecycle event stream synchronization is poisoned")]
234 Poisoned,
235}
236
237struct SubscriberQueue {
238 capacity: usize,
239 state: Mutex<QueueState>,
240 ready: Condvar,
241 lagged: AtomicUsize,
242}
243
244#[derive(Default)]
245struct QueueState {
246 events: VecDeque<LifecycleEvent>,
247 closed: bool,
248}