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 `(Key, Capability)` entry is 24 bytes (`8 B` `FlyStr` key + `16 B`
280/// `Capability`), so 24 * 11 + node metadata = 280 bytes (304-byte Scudo bucket), compared to a
281/// 2-entry `Vec` using 48 bytes (64-byte Scudo bucket).
282#[derive(Debug)]
283pub(crate) enum HybridMap {
284    Vec(Vec<(Key, Capability)>),
285    Map(BTreeMap<Key, Capability>),
286}
287
288impl Default for HybridMap {
289    fn default() -> Self {
290        Self::Vec(Vec::default())
291    }
292}
293
294impl HybridMap {
295    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<Capability>
296    where
297        Key: Borrow<Q> + Ord,
298        Q: Ord,
299    {
300        match self {
301            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, key) {
302                Ok(index) => Some(vec[index].1.clone()),
303                Err(_) => None,
304            },
305            Self::Map(map) => match map.get(key) {
306                Some(capability) => Some(capability.clone()),
307                None => None,
308            },
309        }
310    }
311
312    pub fn insert(
313        &mut self,
314        key: Key,
315        mut capability: Capability,
316        update_notifiers: &mut UpdateNotifiers,
317    ) -> Option<Capability> {
318        match self {
319            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, &key) {
320                Ok(index) => {
321                    update_notifiers.update(EntryUpdate::Remove(&key));
322                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
323                    std::mem::swap(&mut capability, &mut vec[index].1);
324                    Some(capability)
325                }
326                Err(index) => {
327                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
328                    if vec.len() + 1 >= HYBRID_SWITCH_INSERTION_LEN {
329                        self.switch_to_map();
330                        let Self::Map(map) = self else { unreachable!() };
331                        map.insert(key, capability);
332                        None
333                    } else {
334                        vec.reserve_exact(1);
335                        vec.insert(index, (key, capability));
336                        None
337                    }
338                }
339            },
340            Self::Map(map) => match map.entry(key) {
341                Entry::Occupied(mut occupied) => {
342                    update_notifiers.update(EntryUpdate::Remove(occupied.key()));
343                    update_notifiers.update(EntryUpdate::Add(occupied.key(), &capability));
344                    Some(occupied.insert(capability))
345                }
346                Entry::Vacant(vacant) => {
347                    update_notifiers.update(EntryUpdate::Add(vacant.key(), &capability));
348                    vacant.insert(capability);
349                    None
350                }
351            },
352        }
353    }
354
355    pub fn remove(
356        &mut self,
357        key: &BorrowedKey,
358        update_notifiers: &mut UpdateNotifiers,
359    ) -> Option<Capability> {
360        let result = match self {
361            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, key) {
362                Ok(index) => {
363                    update_notifiers.update(EntryUpdate::Remove(key));
364                    Some(vec.remove(index).1)
365                }
366                Err(_) => None,
367            },
368            Self::Map(map) => {
369                let result = map.remove(key);
370                if result.is_some() {
371                    update_notifiers.update(EntryUpdate::Remove(key));
372                    if self.len() <= HYBRID_SWITCH_REMOVAL_LEN {
373                        self.switch_to_vec();
374                    }
375                }
376                result
377            }
378        };
379        result
380    }
381
382    pub fn append(
383        &mut self,
384        other: &Self,
385        update_notifiers: &mut UpdateNotifiers,
386    ) -> Result<(), ()> {
387        if other.is_empty() {
388            // If other is empty then return early.
389            return Ok(());
390        }
391
392        // If any values would be overwritten, throw an error early and don't modify the Dictionary
393        for (k, _) in other.iter() {
394            let contains_key = match self {
395                Self::Vec(vec) => matches!(Self::sorted_vec_index_of(vec, k), Ok(_)),
396                Self::Map(map) => map.contains_key(k),
397            };
398            if contains_key {
399                return Err(());
400            }
401        }
402
403        if self.len() + other.len() >= HYBRID_SWITCH_INSERTION_LEN {
404            // If at some point we will need to switch to a map then do it now.
405            self.switch_to_map();
406        } else if let Self::Vec(vec) = self {
407            // We're currently a Vec and won't need to convert to a Map so grow the Vec to the
408            // final size now.
409            vec.reserve(other.len());
410        }
411        for (k, v) in other.iter() {
412            self.insert(k.clone(), v.clone(), update_notifiers);
413        }
414        Ok(())
415    }
416
417    pub fn shallow_copy(&self) -> Self {
418        match self {
419            Self::Vec(vec) => {
420                let mut new_vec = Vec::with_capacity(vec.len());
421                for (key, value) in vec.iter() {
422                    new_vec.push((key.clone(), value.clone()));
423                }
424                HybridMap::Vec(new_vec)
425            }
426            Self::Map(map) => {
427                let mut new_map = BTreeMap::new();
428                for (key, value) in map.iter() {
429                    new_map.insert(key.clone(), value.clone());
430                }
431                HybridMap::Map(new_map)
432            }
433        }
434    }
435
436    pub fn len(&self) -> usize {
437        match self {
438            Self::Map(map) => map.len(),
439            Self::Vec(vec) => vec.len(),
440        }
441    }
442
443    pub fn is_empty(&self) -> bool {
444        match self {
445            Self::Map(map) => map.is_empty(),
446            Self::Vec(vec) => vec.is_empty(),
447        }
448    }
449
450    pub fn iter(&self) -> impl Iterator<Item = (&Key, &Capability)> {
451        match self {
452            Self::Vec(vec) => itertools::Either::Left(vec.iter().map(|kv| (&kv.0, &kv.1))),
453            Self::Map(map) => itertools::Either::Right(map.iter()),
454        }
455    }
456
457    pub fn into_iter(self) -> impl Iterator<Item = (Key, Capability)> {
458        match self {
459            Self::Vec(vec) => itertools::Either::Left(vec.into_iter()),
460            Self::Map(map) => itertools::Either::Right(map.into_iter()),
461        }
462    }
463
464    fn switch_to_map(&mut self) {
465        match self {
466            Self::Map(_) => {}
467            Self::Vec(vec) => {
468                let vec = std::mem::replace(vec, Vec::new());
469                let map = BTreeMap::from_iter(vec.into_iter());
470                *self = Self::Map(map);
471            }
472        }
473    }
474
475    fn switch_to_vec(&mut self) {
476        match self {
477            Self::Vec(_) => {}
478            Self::Map(map) => {
479                let map = std::mem::replace(map, Default::default());
480                let vec = Vec::from_iter(map.into_iter());
481                *self = Self::Vec(vec);
482            }
483        }
484    }
485
486    #[inline]
487    fn sorted_vec_index_of<Q: ?Sized>(vec: &Vec<(Key, Capability)>, key: &Q) -> Result<usize, usize>
488    where
489        Key: Borrow<Q> + Ord,
490        Q: Ord,
491    {
492        vec.binary_search_by(|probe| probe.0.borrow().cmp(&key))
493    }
494}
495
496#[derive(Default)]
497pub(crate) struct UpdateNotifiers(Vec<UpdateNotifierFn>);
498
499impl UpdateNotifiers {
500    fn update<'a>(&'a mut self, update: EntryUpdate<'a>) {
501        self.0.retain_mut(|notifier_fn| match (notifier_fn)(update) {
502            UpdateNotifierRetention::Retain => true,
503            UpdateNotifierRetention::Drop_ => false,
504        });
505    }
506}
507
508// Tests are located in fidl/dictionary.rs.