vfs/directory/watchers.rs
1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Watchers handles a list of watcher connections attached to a directory. Watchers as described
6//! in fuchsia.io.
7
8pub mod event_producers;
9
10mod watcher;
11pub use watcher::Controller;
12
13use crate::directory::entry_container::{Directory, DirectoryWatcher};
14use crate::directory::watchers::event_producers::EventProducer;
15use crate::execution_scope::ExecutionScope;
16
17use fidl_fuchsia_io as fio;
18
19use slab::Slab;
20use std::sync::Arc;
21
22/// Wraps all watcher connections observing one directory. The directory is responsible for
23/// calling [`Self::add()`] and [`Self::send_event()`] method when appropriate to make sure
24/// watchers are observing a consistent view.
25pub struct Watchers(Slab<Arc<Controller>>);
26
27impl Watchers {
28 /// Constructs a new Watchers instance with no connected watchers.
29 pub fn new() -> Self {
30 Watchers(Slab::new())
31 }
32
33 /// Connects a new watcher (connected over the `channel`) to the list of watchers. It is the
34 /// responsibility of the caller to also send `WATCH_EVENT_EXISTING` and `WatchMask::IDLE`
35 /// events on the returned [`Controller`] to the newly connected watcher using the
36 /// [`Self::send_event`] methods. This `mask` is the event mask this watcher has requested.
37 ///
38 /// Return value of `None` means the executor did not accept a new task, so the watcher has
39 /// been dropped.
40 ///
41 /// NOTE The reason `add` can not send both events on its own by consuming an
42 /// [`EventProducer`] is because a lazy directory needs async context to generate a list of
43 /// it's entries. Meaning we need a async version of the [`EventProducer`] - and that is a lot
44 /// of additional managing of functions and state. Traits do not support async methods yet, so
45 /// we would need to manage futures returned by the [`EventProducer`] methods explicitly.
46 /// Plus, for the [`crate::directory::immutable::Simple`] directory it is all unnecessary.
47 #[must_use = "Caller of add() must send WATCH_EVENT_EXISTING and fio::WatchMask::IDLE on the \
48 returned controller"]
49 pub fn add(
50 &mut self,
51 scope: ExecutionScope,
52 directory: Arc<dyn Directory>,
53 mask: fio::WatchMask,
54 watcher: DirectoryWatcher,
55 ) -> Arc<Controller> {
56 let entry = self.0.vacant_entry();
57 let key = entry.key();
58
59 let done = move || {
60 // Add tests.
61 // TODO(72292)
62 directory.unregister_watcher(key);
63 };
64
65 let controller = Arc::new(watcher::new(scope, mask, watcher, done));
66 entry.insert(controller).clone()
67 }
68
69 /// Informs all the connected watchers about the specified event. While `mask` and `event`
70 /// carry the same information, as they are represented by `WatchMask::*` and `WATCH_EVENT_*`
71 /// constants in fuchsia.io, it is easier when both forms are provided. `mask` is used to
72 /// filter out those watchers that did not request for observation of this event and `event` is
73 /// used to construct the event object. The method will operate correctly only if `mask` and
74 /// `event` match.
75 ///
76 /// In case of a communication error with any of the watchers, connection to this watcher is
77 /// closed.
78 pub fn send_event(&mut self, producer: &mut dyn EventProducer) {
79 while producer.prepare_for_next_buffer() {
80 let mut consumed_any = false;
81
82 for (_key, controller) in self.0.iter() {
83 controller.send_buffer(producer.mask(), || {
84 consumed_any = true;
85 producer.buffer()
86 });
87 }
88
89 if !consumed_any {
90 break;
91 }
92 }
93 }
94
95 /// Disconnects a watcher with the specified key. A directory will use this method during the
96 /// `unregister_watcher` call.
97 pub fn remove(&mut self, key: usize) {
98 self.0.remove(key);
99 }
100}