cu29_logstream/
telemetry.rs1use atomic_waker::AtomicWaker;
15use crossbeam_queue::ArrayQueue;
16use std::{
17 future::poll_fn,
18 num::NonZeroUsize,
19 sync::{
20 Arc,
21 atomic::{AtomicBool, AtomicU64, Ordering},
22 },
23 task::{Poll, Wake, Waker},
24 thread::{self, Thread, ThreadId},
25 time::{Duration, Instant},
26};
27
28struct Sequenced<T> {
29 sequence: u64,
30 frame: T,
31}
32
33struct Shared<T, S> {
34 frames: ArrayQueue<Sequenced<T>>,
35 status: ArrayQueue<S>,
36 waker: AtomicWaker,
37 closed: AtomicBool,
38 connected: AtomicBool,
39 overwritten: AtomicU64,
40}
41
42pub struct TelemetryPublisher<T, S> {
44 shared: Arc<Shared<T, S>>,
45 next_sequence: u64,
46 status: S,
47}
48
49pub struct TelemetryReader<T, S> {
51 shared: Arc<Shared<T, S>>,
52 next_sequence: u64,
53 current: Option<Sequenced<T>>,
54 status: S,
55 thread_waker: Option<(ThreadId, Waker)>,
56}
57
58pub struct TelemetryRead<'a, T> {
63 pub frame: &'a T,
64 pub missed: u64,
65}
66
67pub fn telemetry_channel<T, S: Copy + PartialEq>(
73 capacity: NonZeroUsize,
74 status: S,
75) -> (TelemetryPublisher<T, S>, TelemetryReader<T, S>) {
76 let shared = Arc::new(Shared {
77 frames: ArrayQueue::new(capacity.get()),
78 status: ArrayQueue::new(1),
79 waker: AtomicWaker::new(),
80 closed: AtomicBool::new(false),
81 connected: AtomicBool::new(true),
82 overwritten: AtomicU64::new(0),
83 });
84 shared.status.force_push(status);
85 (
86 TelemetryPublisher {
87 shared: shared.clone(),
88 next_sequence: 0,
89 status,
90 },
91 TelemetryReader {
92 shared,
93 next_sequence: 0,
94 current: None,
95 status,
96 thread_waker: None,
97 },
98 )
99}
100
101impl<T, S: Copy + PartialEq> TelemetryPublisher<T, S> {
102 pub fn publish(&mut self, frame: T) {
105 if !self.is_connected() {
106 return;
107 }
108 let entry = Sequenced {
109 sequence: self.next_sequence,
110 frame,
111 };
112 self.next_sequence = self.next_sequence.wrapping_add(1);
113 if self.shared.frames.force_push(entry).is_some() {
114 self.shared.overwritten.fetch_add(1, Ordering::Relaxed);
115 }
116 self.shared.waker.wake();
117 }
118
119 pub fn set_status(&mut self, status: S) {
121 if self.status != status {
122 self.status = status;
123 self.shared.status.force_push(status);
124 self.shared.waker.wake();
125 }
126 }
127
128 pub fn status(&self) -> S {
129 self.status
130 }
131
132 pub fn is_connected(&self) -> bool {
133 self.shared.connected.load(Ordering::Acquire)
134 }
135
136 pub fn overwritten(&self) -> u64 {
138 self.shared.overwritten.load(Ordering::Relaxed)
139 }
140}
141
142impl<T, S> Drop for TelemetryPublisher<T, S> {
143 fn drop(&mut self) {
144 self.shared.closed.store(true, Ordering::Release);
145 self.shared.waker.wake();
146 }
147}
148
149impl<T, S: Copy> TelemetryReader<T, S> {
150 pub fn try_read(&mut self) -> Option<TelemetryRead<'_, T>> {
154 let entry = self.shared.frames.pop()?;
155 let missed = entry.sequence.wrapping_sub(self.next_sequence);
156 self.next_sequence = entry.sequence.wrapping_add(1);
157 self.current = Some(entry);
158 Some(TelemetryRead {
159 frame: &self.current.as_ref().unwrap().frame,
160 missed,
161 })
162 }
163
164 pub fn status(&mut self) -> S {
167 if let Some(status) = self.shared.status.pop() {
168 self.status = status;
169 }
170 self.status
171 }
172
173 pub fn is_closed(&self) -> bool {
175 self.shared.closed.load(Ordering::Acquire)
176 }
177
178 pub fn overwritten(&self) -> u64 {
179 self.shared.overwritten.load(Ordering::Relaxed)
180 }
181
182 fn is_ready(&self) -> bool {
183 !self.shared.frames.is_empty() || !self.shared.status.is_empty() || self.is_closed()
184 }
185
186 pub fn register_waker(&self, waker: &Waker) {
190 self.shared.waker.register(waker);
191 if self.is_ready() {
192 self.shared.waker.wake();
193 }
194 }
195
196 pub async fn ready(&mut self) {
199 poll_fn(|cx| {
200 self.register_waker(cx.waker());
201 if self.is_ready() {
202 Poll::Ready(())
203 } else {
204 Poll::Pending
205 }
206 })
207 .await;
208 }
209
210 pub fn wait_timeout(&mut self, timeout: Duration) -> bool {
214 let current = thread::current();
215 if self.thread_waker.as_ref().map(|(id, _)| *id) != Some(current.id()) {
216 self.thread_waker = Some((current.id(), Waker::from(Arc::new(ThreadWake(current)))));
217 }
218 let waker = &self.thread_waker.as_ref().unwrap().1;
219 let start = Instant::now();
220 loop {
221 self.register_waker(waker);
222 if self.is_ready() {
223 return true;
224 }
225 let remaining = timeout.saturating_sub(start.elapsed());
226 if remaining.is_zero() {
227 return false;
228 }
229 thread::park_timeout(remaining);
230 }
231 }
232}
233
234impl<T, S> Drop for TelemetryReader<T, S> {
235 fn drop(&mut self) {
236 self.shared.connected.store(false, Ordering::Release);
237 self.shared.waker.take();
238 }
239}
240
241struct ThreadWake(Thread);
242
243impl Wake for ThreadWake {
244 fn wake(self: Arc<Self>) {
245 self.0.unpark();
246 }
247
248 fn wake_by_ref(self: &Arc<Self>) {
249 self.0.unpark();
250 }
251}