1use crate::client::Client;
6use crate::error::{Error, Result};
7use crate::wire::{self, WatchEventKind};
8use std::collections::{HashMap, VecDeque};
9use std::io;
10use std::panic::{catch_unwind, AssertUnwindSafe};
11use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
12use std::sync::{Arc, Condvar, Mutex, MutexGuard};
13use std::time::{Duration, Instant, SystemTime};
14
15const LISTENER_QUEUE: usize = 1024;
17const WARM_CACHE: usize = 1024;
19
20pub type Logger = Arc<dyn Fn(&str) + Send + Sync>;
22
23pub(crate) fn lock<T>(m: &Mutex<T>) -> MutexGuard<'_, T> {
24 m.lock().unwrap_or_else(|e| e.into_inner())
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum EventKind {
31 WarmDone,
33 WarmFailed,
35 Change,
37 Overflow,
40 Closing,
42 ConnectionLost,
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49#[non_exhaustive]
50pub enum ChangeKind {
51 Create,
52 Remove,
53 Write,
54 Rename,
56 Attrib,
57}
58
59#[derive(Debug)]
62#[non_exhaustive]
63pub struct Event {
64 pub kind: EventKind,
65 pub sequence: u64,
67 pub ref_id: u64,
68 pub path: String,
69 pub old_path: String,
70 pub change: Option<ChangeKind>,
72 pub error: Option<Error>,
74 pub time: SystemTime,
76}
77
78impl Clone for Event {
79 fn clone(&self) -> Event {
80 Event {
81 kind: self.kind,
82 sequence: self.sequence,
83 ref_id: self.ref_id,
84 path: self.path.clone(),
85 old_path: self.old_path.clone(),
86 change: self.change,
87 error: self.error.as_ref().map(Error::dup),
88 time: self.time,
89 }
90 }
91}
92
93impl Event {
94 pub(crate) fn local(kind: EventKind, ref_id: u64, error: Option<Error>) -> Event {
95 Event {
96 kind,
97 sequence: 0,
98 ref_id,
99 path: String::new(),
100 old_path: String::new(),
101 change: None,
102 error,
103 time: SystemTime::now(),
104 }
105 }
106
107 pub(crate) fn from_wire(m: wire::Notice) -> Option<Event> {
109 let mut change = None;
110 let kind = match wire::NoticeKind::try_from(m.kind).ok()? {
111 wire::NoticeKind::WarmDone => EventKind::WarmDone,
112 wire::NoticeKind::WarmFailed => EventKind::WarmFailed,
113 wire::NoticeKind::Closing => EventKind::Closing,
114 wire::NoticeKind::Overflow => EventKind::Overflow,
115 wire::NoticeKind::Change => {
116 change = match WatchEventKind::try_from(m.change_kind).ok()? {
117 WatchEventKind::Create => Some(ChangeKind::Create),
118 WatchEventKind::Remove => Some(ChangeKind::Remove),
119 WatchEventKind::Write => Some(ChangeKind::Write),
120 WatchEventKind::Rename => Some(ChangeKind::Rename),
121 WatchEventKind::Attrib => Some(ChangeKind::Attrib),
122 WatchEventKind::Overflow => None,
123 };
124 if change.is_some() {
125 EventKind::Change
126 } else {
127 EventKind::Overflow
128 }
129 }
130 };
131 let error = (kind == EventKind::WarmFailed).then_some(Error::Server {
132 code: m.code,
133 message: m.message,
134 });
135 Some(Event {
136 kind,
137 sequence: m.sequence,
138 ref_id: m.ref_id,
139 path: m.path,
140 old_path: m.old_path,
141 change,
142 error,
143 time: SystemTime::now(),
144 })
145 }
146
147 fn log_line(&self) -> String {
148 let err = self
149 .error
150 .as_ref()
151 .map_or_else(|| "none".to_string(), |e| e.to_string());
152 format!(
153 "event kind={:?} ref_id={} path={:?} error={}",
154 self.kind, self.ref_id, self.path, err
155 )
156 }
157}
158
159#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
161pub struct ListenerId(u64);
162
163struct ChanState {
164 items: VecDeque<Event>,
165 overflowed: bool,
167 closed: bool,
168}
169
170pub(crate) struct Chan {
172 cap: usize,
173 ref_id: AtomicU64,
175 st: Mutex<ChanState>,
176 cv: Condvar,
177}
178
179impl Chan {
180 pub(crate) fn new(cap: usize, ref_id: u64) -> Chan {
181 Chan {
182 cap,
183 ref_id: AtomicU64::new(ref_id),
184 st: Mutex::new(ChanState {
185 items: VecDeque::new(),
186 overflowed: false,
187 closed: false,
188 }),
189 cv: Condvar::new(),
190 }
191 }
192
193 pub(crate) fn set_ref_id(&self, id: u64) {
194 self.ref_id.store(id, Ordering::SeqCst);
195 }
196
197 pub(crate) fn push(&self, ev: Event) -> bool {
200 let mut st = lock(&self.st);
201 if st.closed {
202 return false;
203 }
204 let mut started = false;
205 if st.items.len() >= self.cap {
206 st.items.pop_front();
207 started = !st.overflowed;
208 st.overflowed = true;
209 }
210 st.items.push_back(ev);
211 drop(st);
212 self.cv.notify_one();
213 started
214 }
215
216 pub(crate) fn close(&self, discard: bool) {
218 let mut st = lock(&self.st);
219 st.closed = true;
220 if discard {
221 st.items.clear();
222 st.overflowed = false;
223 }
224 drop(st);
225 self.cv.notify_all();
226 }
227
228 pub(crate) fn pop(&self, timeout: Option<Duration>) -> Option<Event> {
231 let deadline = timeout.and_then(|t| Instant::now().checked_add(t));
232 let mut st = lock(&self.st);
233 loop {
234 if st.overflowed {
235 st.overflowed = false;
236 return Some(Event::local(
237 EventKind::Overflow,
238 self.ref_id.load(Ordering::SeqCst),
239 None,
240 ));
241 }
242 if let Some(ev) = st.items.pop_front() {
243 return Some(ev);
244 }
245 if st.closed {
246 return None;
247 }
248 st = match deadline {
249 None => self.cv.wait(st).unwrap_or_else(|e| e.into_inner()),
250 Some(d) => {
251 let left = d.saturating_duration_since(Instant::now());
252 if left.is_zero() {
253 return None;
254 }
255 self.cv.wait_timeout(st, left).unwrap_or_else(|e| e.into_inner()).0
256 }
257 };
258 }
259 }
260}
261
262type Listener = Arc<dyn Fn(&Event) + Send + Sync>;
263
264#[derive(Default)]
265struct Warms {
266 done: HashMap<u64, std::result::Result<(), (String, String)>>,
268 order: VecDeque<u64>,
269 closed: bool,
270 instance: u64,
272 instance_known: bool,
275 restarts: u64,
277}
278
279struct WatchEntry {
280 conn: u64,
281 chan: Arc<Chan>,
282}
283
284pub(crate) struct Hub {
286 logger: Logger,
287 listeners: Mutex<Vec<(ListenerId, Listener)>>,
288 next_listener: AtomicU64,
289 queue: Chan,
290 warms: Mutex<Warms>,
291 warm_cv: Condvar,
292 watches: Mutex<HashMap<u64, WatchEntry>>,
293 threads: Arc<AtomicUsize>,
295}
296
297pub(crate) struct ThreadGuard(Arc<AtomicUsize>);
299
300impl Drop for ThreadGuard {
301 fn drop(&mut self) {
302 self.0.fetch_sub(1, Ordering::SeqCst);
303 }
304}
305
306impl Hub {
307 pub(crate) fn thread_guard(&self) -> ThreadGuard {
309 self.threads.fetch_add(1, Ordering::SeqCst);
310 ThreadGuard(self.threads.clone())
311 }
312
313 pub(crate) fn thread_probe(&self) -> Arc<AtomicUsize> {
314 self.threads.clone()
315 }
316
317 pub(crate) fn new(logger: Option<Logger>) -> Hub {
318 Hub {
319 logger: logger.unwrap_or_else(|| Arc::new(|line: &str| eprintln!("mountos: {line}"))),
320 listeners: Mutex::new(Vec::new()),
321 next_listener: AtomicU64::new(1),
322 queue: Chan::new(LISTENER_QUEUE, 0),
323 warms: Mutex::new(Warms::default()),
324 warm_cv: Condvar::new(),
325 watches: Mutex::new(HashMap::new()),
326 threads: Arc::new(AtomicUsize::new(0)),
327 }
328 }
329
330 pub(crate) fn log(&self, line: &str) {
331 (self.logger)(line);
332 }
333
334 pub(crate) fn add_watch(&self, id: u64, conn: u64, chan: Arc<Chan>) {
335 chan.set_ref_id(id);
336 lock(&self.watches).insert(id, WatchEntry { conn, chan });
337 }
338
339 pub(crate) fn remove_watch(&self, id: u64) {
340 lock(&self.watches).remove(&id);
341 }
342
343 pub(crate) fn conn_lost(&self, conn: u64) {
345 lock(&self.watches).retain(|_, w| {
346 if w.conn == conn {
347 w.chan.close(false);
348 false
349 } else {
350 true
351 }
352 });
353 }
354
355 pub(crate) fn publish(&self, conn: Option<u64>, ev: Event) {
359 match ev.kind {
360 EventKind::WarmDone | EventKind::WarmFailed => self.record_warm(&ev),
361 EventKind::Overflow if ev.ref_id == 0 => {
362 if let Some(conn) = conn {
363 let chans: Vec<Arc<Chan>> = lock(&self.watches)
364 .values()
365 .filter(|w| w.conn == conn)
366 .map(|w| w.chan.clone())
367 .collect();
368 for c in chans {
369 c.push(ev.clone());
370 }
371 }
372 }
373 EventKind::Change | EventKind::Overflow if ev.ref_id != 0 => {
374 let chan = lock(&self.watches).get(&ev.ref_id).map(|w| w.chan.clone());
375 if let Some(c) = chan {
376 if c.push(ev.clone()) {
377 self.log(&format!("watch {} queue full, dropped oldest events", ev.ref_id));
378 }
379 }
380 }
381 _ => {}
382 }
383 if self.queue.push(ev) {
384 self.log("listener queue full, dropped oldest events");
385 }
386 }
387
388 fn record_warm(&self, ev: &Event) {
389 let r = match &ev.error {
390 Some(Error::Server { code, message }) => Err((code.clone(), message.clone())),
391 _ => Ok(()),
392 };
393 let mut w = lock(&self.warms);
394 if w.done.insert(ev.ref_id, r).is_none() {
395 w.order.push_back(ev.ref_id);
396 }
397 while w.order.len() > WARM_CACHE {
398 if let Some(old) = w.order.pop_front() {
399 w.done.remove(&old);
400 }
401 }
402 drop(w);
403 self.warm_cv.notify_all();
404 }
405
406 pub(crate) fn note_instance(&self, id: u64) {
411 let mut w = lock(&self.warms);
412 let known = std::mem::replace(&mut w.instance_known, true);
413 if known && w.instance == id {
414 return;
415 }
416 w.instance = id;
417 if !known {
418 return;
419 }
420 w.done.clear();
421 w.order.clear();
422 w.restarts += 1;
423 drop(w);
424 self.warm_cv.notify_all();
425 }
426
427 pub(crate) fn wait_warm(&self, id: u64, timeout: Duration) -> Result<()> {
428 let deadline = Instant::now().checked_add(timeout);
429 let mut w = lock(&self.warms);
430 let restarts = w.restarts;
431 loop {
432 if w.restarts != restarts {
433 return Err(Error::Io(io::Error::new(
434 io::ErrorKind::ConnectionAborted,
435 "server restarted, warm result lost",
436 )));
437 }
438 if w.closed {
439 return Err(Error::Io(io::Error::new(
440 io::ErrorKind::ConnectionAborted,
441 "client closed",
442 )));
443 }
444 match w.done.get(&id) {
445 Some(Ok(())) => return Ok(()),
446 Some(Err((code, message))) => {
447 return Err(Error::Server {
448 code: code.clone(),
449 message: message.clone(),
450 })
451 }
452 None => {}
453 }
454 let left = match deadline {
455 Some(d) => d.saturating_duration_since(Instant::now()),
456 None => Duration::from_secs(3600),
457 };
458 if left.is_zero() {
459 return Err(Error::Io(io::Error::new(
460 io::ErrorKind::TimedOut,
461 "wait_warm timed out",
462 )));
463 }
464 w = self.warm_cv.wait_timeout(w, left).unwrap_or_else(|e| e.into_inner()).0;
465 }
466 }
467
468 pub(crate) fn shutdown(&self) {
470 self.queue.close(true);
471 for (_, w) in lock(&self.watches).drain() {
472 w.chan.close(false);
473 }
474 lock(&self.warms).closed = true;
475 self.warm_cv.notify_all();
476 }
477
478 pub(crate) fn clear_listeners(&self) {
480 let old = std::mem::take(&mut *lock(&self.listeners));
481 drop(old);
482 }
483
484 pub(crate) fn dispatch_loop(&self) {
486 while let Some(ev) = self.queue.pop(None) {
487 let ls: Vec<Listener> = lock(&self.listeners).iter().map(|(_, l)| l.clone()).collect();
488 if ls.is_empty() {
489 self.log(&ev.log_line());
490 continue;
491 }
492 for l in ls {
493 if catch_unwind(AssertUnwindSafe(|| l(&ev))).is_err() {
494 self.log(&format!("listener panicked on {}", ev.log_line()));
495 }
496 }
497 }
498 }
499}
500
501impl Client {
502 pub fn add_listener(&self, f: impl Fn(&Event) + Send + Sync + 'static) -> ListenerId {
508 let hub = &self.inner.hub;
509 let id = ListenerId(hub.next_listener.fetch_add(1, Ordering::SeqCst));
510 lock(&hub.listeners).push((id, Arc::new(f)));
511 id
512 }
513
514 #[doc(hidden)]
516 pub fn thread_probe(&self) -> Arc<AtomicUsize> {
517 self.inner.hub.thread_probe()
518 }
519
520 pub fn remove_listener(&self, id: ListenerId) {
522 lock(&self.inner.hub.listeners).retain(|(i, _)| *i != id);
523 }
524
525 pub fn wait_warm(&self, id: u64, timeout: Duration) -> Result<()> {
530 self.inner.hub.wait_warm(id, timeout)
531 }
532}