Skip to main content

runtime_capabilities/
dictionary.rs

1// Copyright 2023 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
5use crate::{Capability, CapabilityBound};
6use derivative::Derivative;
7use fuchsia_sync::{Mutex, MutexGuard};
8use std::borrow::Borrow;
9use std::collections::BTreeMap;
10use std::collections::btree_map::Entry;
11use std::sync::Arc;
12
13#[cfg(target_os = "fuchsia")]
14use fuchsia_async as fasync;
15
16pub type Key = cm_types::Name;
17pub type BorrowedKey = cm_types::BorrowedName;
18
19/// A capability that represents a dictionary of capabilities.
20#[derive(Debug)]
21pub struct Dictionary {
22    inner: Mutex<DictInner>,
23}
24
25#[derive(Derivative)]
26#[derivative(Debug)]
27pub(crate) struct DictInner {
28    /// The contents of the [Dictionary].
29    pub(crate) entries: HybridMap,
30
31    /// When an external request tries to access a non-existent entry, this closure will be invoked
32    /// with the name of the entry.
33    #[derivative(Debug = "ignore")]
34    // Currently this is only used on target, but it's compatible with host.
35    #[allow(dead_code)]
36    pub(crate) not_found: Option<Box<dyn Fn(&str) -> () + 'static + Send + Sync>>,
37
38    /// Tasks that serve the Dictionary protocol. This is an `Option` because dictionaries are not
39    /// necessarily used in async contexts but a Scope will fail construction if there is no
40    /// async executor.
41    #[cfg(target_os = "fuchsia")]
42    #[derivative(Debug = "ignore")]
43    task_group: Option<fasync::Scope>,
44
45    /// Functions that will be invoked whenever the contents of this dictionary changes.
46    #[derivative(Debug = "ignore")]
47    update_notifiers: UpdateNotifiers,
48}
49
50impl CapabilityBound for Dictionary {
51    fn debug_typename() -> &'static str {
52        "Dictionary"
53    }
54
55    #[cfg(target_os = "fuchsia")]
56    fn try_into_directory_entry(
57        self: Arc<Self>,
58        scope: vfs::execution_scope::ExecutionScope,
59        token: Arc<crate::WeakInstanceToken>,
60    ) -> Result<Arc<dyn vfs::directory::entry::DirectoryEntry>, crate::ConversionError> {
61        self.try_into_directory_entry_inner(scope, token)
62    }
63}
64
65impl Drop for DictInner {
66    fn drop(&mut self) {
67        // When this dictionary doesn't exist anymore, then neither do its entries (or at least
68        // these references of them). Notify anyone listening that all of the entries have been
69        // removed.
70        let entries = std::mem::replace(&mut self.entries, HybridMap::default());
71        for (key, _) in entries.into_iter() {
72            self.update_notifiers.update(EntryUpdate::Remove(&key))
73        }
74    }
75}
76
77/// Represents a change to a dictionary, where an entry is either added or removed.
78#[derive(Debug, Copy, Clone)]
79pub enum EntryUpdate<'a> {
80    Add(&'a BorrowedKey, &'a Capability),
81    Remove(&'a BorrowedKey),
82    Idle,
83}
84
85/// Represents whether an update notifier should be retained and thus continue to be called for
86/// future updates, or if it should be dropped and no longer be called.
87pub enum UpdateNotifierRetention {
88    Retain,
89    Drop_,
90}
91
92/// A function that will be called when the contents of a dictionary changes. Note that this
93/// function will be called while the internal dictionary structure is locked, so it must not
94/// interact with the dictionary on which it is registered. It shouldn't even hold a strong
95/// reference to the dictionary it's registered on, as that'll create a cycle and make LSAN sad.
96pub type UpdateNotifierFn =
97    Box<dyn for<'a> FnMut(EntryUpdate<'a>) -> UpdateNotifierRetention + Sync + Send>;
98
99impl Default for Dictionary {
100    fn default() -> Self {
101        Self {
102            inner: Mutex::new(DictInner {
103                entries: HybridMap::default(),
104                not_found: None,
105                #[cfg(target_os = "fuchsia")]
106                task_group: None,
107                update_notifiers: UpdateNotifiers::default(),
108            }),
109        }
110    }
111}
112
113impl Dictionary {
114    /// Creates an empty dictionary.
115    pub fn new() -> Arc<Self> {
116        Arc::new(Self::default())
117    }
118
119    /// Creates an empty dictionary. When an external request tries to access a non-existent entry,
120    /// the name of the entry will be sent using `not_found`.
121    pub fn new_with_not_found(not_found: impl Fn(&str) -> () + 'static + Send + Sync) -> Arc<Self> {
122        Arc::new(Self {
123            inner: Mutex::new(DictInner {
124                entries: HybridMap::default(),
125                not_found: Some(Box::new(not_found)),
126                #[cfg(target_os = "fuchsia")]
127                task_group: None,
128                update_notifiers: UpdateNotifiers::default(),
129            }),
130        })
131    }
132
133    pub(crate) fn lock(&self) -> MutexGuard<'_, DictInner> {
134        self.inner.lock()
135    }
136
137    /// Registers a new update notifier function with this dictionary. The function will be called
138    /// whenever an entry is added to or removed from this dictionary. The function will be
139    /// immediately called with an `EntryUpdate::Add` value for any entries already in this
140    /// dictionary.
141    ///
142    /// Note that this function will be called while the internal dictionary structure is locked,
143    /// so it must not interact with the dictionary on which it is registered. It shouldn't even
144    /// hold a strong reference to the dictionary it's registered on, as that'll create a cycle and
145    /// make LSAN sad.
146    pub fn register_update_notifier(&self, mut notifier_fn: UpdateNotifierFn) {
147        let mut guard = self.lock();
148        for (key, value) in guard.entries.iter() {
149            if let UpdateNotifierRetention::Drop_ = (notifier_fn)(EntryUpdate::Add(key, value)) {
150                // The notifier has signaled that it doesn't want to process any more updates.
151                return;
152            }
153        }
154        if let UpdateNotifierRetention::Retain = (notifier_fn)(EntryUpdate::Idle) {
155            guard.update_notifiers.0.push(notifier_fn);
156        }
157    }
158
159    /// Inserts an entry, mapping `key` to `capability`. If an entry already exists at `key`, the
160    /// old value will be returned.
161    pub fn insert(&self, key: Key, capability: Capability) -> Option<Capability> {
162        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
163        entries.insert(key, capability, update_notifiers)
164    }
165
166    pub fn append(&self, other: &Dictionary) -> Result<(), ()> {
167        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
168        let other = other.lock();
169        entries.append(&other.entries, update_notifiers)
170    }
171
172    /// Returns a clone of the capability associated with `key`. If there is no entry for `key`,
173    /// `None` is returned.
174    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<Capability>
175    where
176        Key: Borrow<Q> + Ord,
177        Q: Ord,
178    {
179        self.lock().entries.get(key)
180    }
181
182    /// Returns a clone of the capability associated with `key`, or populates it with `default` if
183    /// it is not present.
184    pub fn get_or_insert(&self, key: &Key, default: impl FnOnce() -> Capability) -> Capability {
185        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
186        match entries.get(key) {
187            Some(v) => v,
188            None => {
189                let v = (default)();
190                entries.insert(key.clone(), v.clone(), update_notifiers);
191                v
192            }
193        }
194    }
195
196    /// Removes `key` from the entries, returning the capability at `key` if the key was already in
197    /// the entries.
198    pub fn remove(&self, key: &BorrowedKey) -> Option<Capability> {
199        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
200        entries.remove(key, update_notifiers)
201    }
202
203    /// Returns an iterator over a clone of the entries, sorted by key.
204    pub fn enumerate(&self) -> impl Iterator<Item = (Key, Capability)> + use<> {
205        self.lock()
206            .entries
207            .iter()
208            .map(|(k, v)| (k.clone(), v.clone()))
209            .collect::<Vec<_>>()
210            .into_iter()
211    }
212
213    /// Returns the keys.
214    pub fn snapshot_keys_as_strings(&self) -> Vec<String> {
215        let guard = self.lock();
216        match &guard.entries {
217            HybridMap::Vec(vec) => vec.iter().map(|(k, _)| k.to_string()).collect(),
218            HybridMap::Map(map) => map.keys().map(|k| k.to_string()).collect(),
219        }
220    }
221
222    /// Returns if entries is empty.
223    pub fn is_empty(&self) -> bool {
224        self.lock().entries.is_empty()
225    }
226
227    /// Removes all entries from the Dictionary and returns them as an iterator.
228    pub fn drain(&self) -> impl Iterator<Item = (Key, Capability)> + use<> {
229        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
230        let entries = std::mem::replace(entries, HybridMap::default());
231        for (key, _) in entries.iter() {
232            update_notifiers.update(EntryUpdate::Remove(&key))
233        }
234        entries.into_iter()
235    }
236
237    /// Creates a new Dictionary with entries cloned from this Dictionary.
238    ///
239    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
240    /// underlying data.
241    pub fn shallow_copy(&self) -> Arc<Self> {
242        let copy = Self::new();
243        {
244            let DictInner { entries, .. } = &*self.lock();
245            let mut copy = copy.lock();
246            copy.entries = entries.shallow_copy();
247        }
248        copy
249    }
250
251    /// Sends the name of an entry to the not found handler.
252    // Currently this is only used on target, but it's compatible with host.
253    #[allow(dead_code)]
254    pub(crate) fn not_found(&self, entry: &str) {
255        if let Some(not_found_handler) = &self.lock().not_found {
256            not_found_handler(entry);
257        }
258    }
259}
260
261impl DictInner {
262    #[cfg(target_os = "fuchsia")]
263    pub(crate) fn tasks(&mut self) -> &mut fasync::Scope {
264        self.task_group.get_or_insert_with(|| fasync::Scope::new())
265    }
266}
267
268pub(crate) const HYBRID_SWITCH_INSERTION_LEN: usize = 11;
269pub(crate) const HYBRID_SWITCH_REMOVAL_LEN: usize = 5;
270
271/// A map collection whose representation is a `Vec` for small `N` and a `BTreeMap` for larger `N`,
272/// where the threshold is defined by the constant `HYBRID_SWITCH_INSERTION_LEN` for insertion and
273/// `HYBRID_SWITCH_REMOVAL_LEN` for removal. This is a more space efficient representation for
274/// small `N` than a `BTreeMap`, which has a big impact because component_manager creates lots of
275/// `Dictionary` objects for component sandboxes.
276///
277/// Details: Rust's `BTreeMap` implementation uses a `B` value of 6, which means each node in the
278/// `BTreeMap` reserves space for 11 entries. Inserting 1 entry will allocate a node with space for
279/// 11 entries. Each entry is 48 bytes, 48 * 11 + some metadata = 544 bytes. Rounding up to the
280/// nearest scudo bucket size means each node consumes 656 bytes of memory.
281///
282/// A BTreeMap with only 2 entries uses 656 bytes when 112 would be sufficient (48 * 2 = 96 fits in
283/// the 112 byte scudo bucket).
284#[derive(Debug)]
285pub(crate) enum HybridMap {
286    Vec(Vec<(Key, Capability)>),
287    Map(BTreeMap<Key, Capability>),
288}
289
290impl Default for HybridMap {
291    fn default() -> Self {
292        Self::Vec(Vec::default())
293    }
294}
295
296impl HybridMap {
297    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<Capability>
298    where
299        Key: Borrow<Q> + Ord,
300        Q: Ord,
301    {
302        match self {
303            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, key) {
304                Ok(index) => Some(vec[index].1.clone()),
305                Err(_) => None,
306            },
307            Self::Map(map) => match map.get(key) {
308                Some(capability) => Some(capability.clone()),
309                None => None,
310            },
311        }
312    }
313
314    pub fn insert(
315        &mut self,
316        key: Key,
317        mut capability: Capability,
318        update_notifiers: &mut UpdateNotifiers,
319    ) -> Option<Capability> {
320        match self {
321            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, &key) {
322                Ok(index) => {
323                    update_notifiers.update(EntryUpdate::Remove(&key));
324                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
325                    std::mem::swap(&mut capability, &mut vec[index].1);
326                    Some(capability)
327                }
328                Err(index) => {
329                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
330                    if vec.len() + 1 >= HYBRID_SWITCH_INSERTION_LEN {
331                        self.switch_to_map();
332                        let Self::Map(map) = self else { unreachable!() };
333                        map.insert(key, capability);
334                        None
335                    } else {
336                        vec.reserve_exact(1);
337                        vec.insert(index, (key, capability));
338                        None
339                    }
340                }
341            },
342            Self::Map(map) => match map.entry(key) {
343                Entry::Occupied(mut occupied) => {
344                    update_notifiers.update(EntryUpdate::Remove(occupied.key()));
345                    update_notifiers.update(EntryUpdate::Add(occupied.key(), &capability));
346                    Some(occupied.insert(capability))
347                }
348                Entry::Vacant(vacant) => {
349                    update_notifiers.update(EntryUpdate::Add(vacant.key(), &capability));
350                    vacant.insert(capability);
351                    None
352                }
353            },
354        }
355    }
356
357    pub fn remove(
358        &mut self,
359        key: &BorrowedKey,
360        update_notifiers: &mut UpdateNotifiers,
361    ) -> Option<Capability> {
362        let result = match self {
363            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, key) {
364                Ok(index) => {
365                    update_notifiers.update(EntryUpdate::Remove(key));
366                    Some(vec.remove(index).1)
367                }
368                Err(_) => None,
369            },
370            Self::Map(map) => {
371                let result = map.remove(key);
372                if result.is_some() {
373                    update_notifiers.update(EntryUpdate::Remove(key));
374                    if self.len() <= HYBRID_SWITCH_REMOVAL_LEN {
375                        self.switch_to_vec();
376                    }
377                }
378                result
379            }
380        };
381        result
382    }
383
384    pub fn append(
385        &mut self,
386        other: &Self,
387        update_notifiers: &mut UpdateNotifiers,
388    ) -> Result<(), ()> {
389        if other.is_empty() {
390            // If other is empty then return early.
391            return Ok(());
392        }
393
394        // If any values would be overwritten, throw an error early and don't modify the Dictionary
395        for (k, _) in other.iter() {
396            let contains_key = match self {
397                Self::Vec(vec) => matches!(Self::sorted_vec_index_of(vec, k), Ok(_)),
398                Self::Map(map) => map.contains_key(k),
399            };
400            if contains_key {
401                return Err(());
402            }
403        }
404
405        if self.len() + other.len() >= HYBRID_SWITCH_INSERTION_LEN {
406            // If at some point we will need to switch to a map then do it now.
407            self.switch_to_map();
408        } else if let Self::Vec(vec) = self {
409            // We're currently a Vec and won't need to convert to a Map so grow the Vec to the
410            // final size now.
411            vec.reserve(other.len());
412        }
413        for (k, v) in other.iter() {
414            self.insert(k.clone(), v.clone(), update_notifiers);
415        }
416        Ok(())
417    }
418
419    pub fn shallow_copy(&self) -> Self {
420        match self {
421            Self::Vec(vec) => {
422                let mut new_vec = Vec::with_capacity(vec.len());
423                for (key, value) in vec.iter() {
424                    new_vec.push((key.clone(), value.clone()));
425                }
426                HybridMap::Vec(new_vec)
427            }
428            Self::Map(map) => {
429                let mut new_map = BTreeMap::new();
430                for (key, value) in map.iter() {
431                    new_map.insert(key.clone(), value.clone());
432                }
433                HybridMap::Map(new_map)
434            }
435        }
436    }
437
438    pub fn len(&self) -> usize {
439        match self {
440            Self::Map(map) => map.len(),
441            Self::Vec(vec) => vec.len(),
442        }
443    }
444
445    pub fn is_empty(&self) -> bool {
446        match self {
447            Self::Map(map) => map.is_empty(),
448            Self::Vec(vec) => vec.is_empty(),
449        }
450    }
451
452    pub fn iter(&self) -> impl Iterator<Item = (&Key, &Capability)> {
453        match self {
454            Self::Vec(vec) => itertools::Either::Left(vec.iter().map(|kv| (&kv.0, &kv.1))),
455            Self::Map(map) => itertools::Either::Right(map.iter()),
456        }
457    }
458
459    pub fn into_iter(self) -> impl Iterator<Item = (Key, Capability)> {
460        match self {
461            Self::Vec(vec) => itertools::Either::Left(vec.into_iter()),
462            Self::Map(map) => itertools::Either::Right(map.into_iter()),
463        }
464    }
465
466    fn switch_to_map(&mut self) {
467        match self {
468            Self::Map(_) => {}
469            Self::Vec(vec) => {
470                let vec = std::mem::replace(vec, Vec::new());
471                let map = BTreeMap::from_iter(vec.into_iter());
472                *self = Self::Map(map);
473            }
474        }
475    }
476
477    fn switch_to_vec(&mut self) {
478        match self {
479            Self::Vec(_) => {}
480            Self::Map(map) => {
481                let map = std::mem::replace(map, Default::default());
482                let vec = Vec::from_iter(map.into_iter());
483                *self = Self::Vec(vec);
484            }
485        }
486    }
487
488    #[inline]
489    fn sorted_vec_index_of<Q: ?Sized>(vec: &Vec<(Key, Capability)>, key: &Q) -> Result<usize, usize>
490    where
491        Key: Borrow<Q> + Ord,
492        Q: Ord,
493    {
494        vec.binary_search_by(|probe| probe.0.borrow().cmp(&key))
495    }
496}
497
498#[derive(Default)]
499pub(crate) struct UpdateNotifiers(Vec<UpdateNotifierFn>);
500
501impl UpdateNotifiers {
502    fn update<'a>(&'a mut self, update: EntryUpdate<'a>) {
503        self.0.retain_mut(|notifier_fn| match (notifier_fn)(update) {
504            UpdateNotifierRetention::Retain => true,
505            UpdateNotifierRetention::Drop_ => false,
506        });
507    }
508}
509
510// Tests are located in fidl/dictionary.rs.