a1ed8d8fc5
One socket on :7700 carries hello, heartbeats both ways (5 s timeout), 5 Hz state snapshots of envelopes, and idempotent commands. 'mark' is forwarded to the recorder's control socket. --mock synthesises degrading state flagged as mock in every envelope; without it a client gets hello and heartbeats only. Peers outside loopback, RFC 1918 and the tailnet are refused. Deployed on the car Orin as yukart-gw.service. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
59 lines
1.5 KiB
Rust
59 lines
1.5 KiB
Rust
//! Fan-out to clients. Drop-only: each client has a bounded queue that loses its
|
|
//! oldest message when full, so a tablet with its screen off never stalls anything.
|
|
|
|
use std::collections::VecDeque;
|
|
use std::sync::{Arc, Condvar, Mutex};
|
|
use std::time::Duration;
|
|
|
|
const QUEUE: usize = 64;
|
|
|
|
pub struct Outbox {
|
|
q: Mutex<VecDeque<String>>,
|
|
cv: Condvar,
|
|
}
|
|
|
|
impl Outbox {
|
|
pub fn push(&self, msg: String) {
|
|
let mut q = self.q.lock().unwrap();
|
|
if q.len() >= QUEUE {
|
|
q.pop_front();
|
|
}
|
|
q.push_back(msg);
|
|
self.cv.notify_one();
|
|
}
|
|
|
|
/// Everything queued, waiting at most `wait` for something to arrive.
|
|
pub fn drain(&self, wait: Duration) -> Vec<String> {
|
|
let q = self.q.lock().unwrap();
|
|
let (mut q, _) = self.cv.wait_timeout_while(q, wait, |q| q.is_empty()).unwrap();
|
|
q.drain(..).collect()
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Clone)]
|
|
pub struct Hub {
|
|
clients: Arc<Mutex<Vec<Arc<Outbox>>>>,
|
|
}
|
|
|
|
impl Hub {
|
|
pub fn join(&self) -> Arc<Outbox> {
|
|
let o = Arc::new(Outbox { q: Mutex::new(VecDeque::new()), cv: Condvar::new() });
|
|
self.clients.lock().unwrap().push(o.clone());
|
|
o
|
|
}
|
|
|
|
pub fn leave(&self, o: &Arc<Outbox>) {
|
|
self.clients.lock().unwrap().retain(|c| !Arc::ptr_eq(c, o));
|
|
}
|
|
|
|
pub fn broadcast(&self, msg: &str) {
|
|
for c in self.clients.lock().unwrap().iter() {
|
|
c.push(msg.to_string());
|
|
}
|
|
}
|
|
|
|
pub fn count(&self) -> usize {
|
|
self.clients.lock().unwrap().len()
|
|
}
|
|
}
|