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}