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 fidl_fuchsia_component_sandbox as fsandbox;
8use fuchsia_sync::{Mutex, MutexGuard};
9use std::borrow::Borrow;
10use std::collections::BTreeMap;
11use std::collections::btree_map::Entry;
12use std::sync::Arc;
13
14#[cfg(target_os = "fuchsia")]
15use fuchsia_async as fasync;
16
17pub type Key = cm_types::Name;
18pub type BorrowedKey = cm_types::BorrowedName;
19
20/// A capability that represents a dictionary of capabilities.
21#[derive(Debug, Clone)]
22pub struct Dictionary {
23    inner: Arc<Mutex<DictInner>>,
24}
25
26#[derive(Derivative)]
27#[derivative(Debug)]
28pub(crate) struct DictInner {
29    /// The contents of the [Dictionary].
30    pub(crate) entries: HybridMap,
31
32    /// When an external request tries to access a non-existent entry, this closure will be invoked
33    /// with the name of the entry.
34    #[derivative(Debug = "ignore")]
35    // Currently this is only used on target, but it's compatible with host.
36    #[allow(dead_code)]
37    pub(crate) not_found: Option<Box<dyn Fn(&str) -> () + 'static + Send + Sync>>,
38
39    /// Tasks that serve the Dictionary protocol. This is an `Option` because dictionaries are not
40    /// necessarily used in async contexts but a TaskGroup will fail construction if there is no
41    /// async executor.
42    #[cfg(target_os = "fuchsia")]
43    #[derivative(Debug = "ignore")]
44    task_group: Option<fasync::TaskGroup>,
45
46    /// Functions that will be invoked whenever the contents of this dictionary changes.
47    #[derivative(Debug = "ignore")]
48    update_notifiers: UpdateNotifiers,
49}
50
51impl CapabilityBound for Dictionary {
52    fn debug_typename() -> &'static str {
53        "Dictionary"
54    }
55}
56
57impl Drop for DictInner {
58    fn drop(&mut self) {
59        // When this dictionary doesn't exist anymore, then neither do its entries (or at least
60        // these references of them). Notify anyone listening that all of the entries have been
61        // removed.
62        let entries = std::mem::replace(&mut self.entries, HybridMap::default());
63        for (key, _) in entries.into_iter() {
64            self.update_notifiers.update(EntryUpdate::Remove(&key))
65        }
66    }
67}
68
69/// Represents a change to a dictionary, where an entry is either added or removed.
70#[derive(Debug, Copy, Clone)]
71pub enum EntryUpdate<'a> {
72    Add(&'a BorrowedKey, &'a Capability),
73    Remove(&'a BorrowedKey),
74    Idle,
75}
76
77/// Represents whether an update notifier should be retained and thus continue to be called for
78/// future updates, or if it should be dropped and no longer be called.
79pub enum UpdateNotifierRetention {
80    Retain,
81    Drop_,
82}
83
84/// A function that will be called when the contents of a dictionary changes. Note that this
85/// function will be called while the internal dictionary structure is locked, so it must not
86/// interact with the dictionary on which it is registered. It shouldn't even hold a strong
87/// reference to the dictionary it's registered on, as that'll create a cycle and make LSAN sad.
88pub type UpdateNotifierFn =
89    Box<dyn for<'a> FnMut(EntryUpdate<'a>) -> UpdateNotifierRetention + Sync + Send>;
90
91impl Default for Dictionary {
92    fn default() -> Self {
93        Self {
94            inner: Arc::new(Mutex::new(DictInner {
95                entries: HybridMap::default(),
96                not_found: None,
97                #[cfg(target_os = "fuchsia")]
98                task_group: None,
99                update_notifiers: UpdateNotifiers::default(),
100            })),
101        }
102    }
103}
104
105impl Dictionary {
106    /// Creates an empty dictionary.
107    pub fn new() -> Self {
108        Self::default()
109    }
110
111    /// Creates an empty dictionary. When an external request tries to access a non-existent entry,
112    /// the name of the entry will be sent using `not_found`.
113    pub fn new_with_not_found(not_found: impl Fn(&str) -> () + 'static + Send + Sync) -> Self {
114        Self {
115            inner: Arc::new(Mutex::new(DictInner {
116                entries: HybridMap::default(),
117                not_found: Some(Box::new(not_found)),
118                #[cfg(target_os = "fuchsia")]
119                task_group: None,
120                update_notifiers: UpdateNotifiers::default(),
121            })),
122        }
123    }
124
125    pub(crate) fn lock(&self) -> MutexGuard<'_, DictInner> {
126        self.inner.lock()
127    }
128
129    /// Registers a new update notifier function with this dictionary. The function will be called
130    /// whenever an entry is added to or removed from this dictionary. The function will be
131    /// immediately called with an `EntryUpdate::Add` value for any entries already in this
132    /// dictionary.
133    ///
134    /// Note that this function will be called while the internal dictionary structure is locked,
135    /// so it must not interact with the dictionary on which it is registered. It shouldn't even
136    /// hold a strong reference to the dictionary it's registered on, as that'll create a cycle and
137    /// make LSAN sad.
138    pub fn register_update_notifier(&self, mut notifier_fn: UpdateNotifierFn) {
139        let mut guard = self.lock();
140        for (key, value) in guard.entries.iter() {
141            if let UpdateNotifierRetention::Drop_ = (notifier_fn)(EntryUpdate::Add(key, value)) {
142                // The notifier has signaled that it doesn't want to process any more updates.
143                return;
144            }
145        }
146        if let UpdateNotifierRetention::Retain = (notifier_fn)(EntryUpdate::Idle) {
147            guard.update_notifiers.0.push(notifier_fn);
148        }
149    }
150
151    /// Inserts an entry, mapping `key` to `capability`. If an entry already exists at `key`, a
152    /// `fsandbox::DictionaryError::AlreadyExists` will be returned.
153    pub fn insert(
154        &self,
155        key: Key,
156        capability: Capability,
157    ) -> Result<(), fsandbox::CapabilityStoreError> {
158        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
159        entries.insert(key, capability, update_notifiers)
160    }
161
162    pub fn append(&self, other: &Dictionary) -> Result<(), ()> {
163        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
164        let other = other.lock();
165        entries.append(&other.entries, update_notifiers)
166    }
167
168    /// Returns a clone of the capability associated with `key`. If there is no entry for `key`,
169    /// `None` is returned.
170    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<Capability>
171    where
172        Key: Borrow<Q> + Ord,
173        Q: Ord,
174    {
175        self.lock().entries.get(key)
176    }
177
178    /// Returns a clone of the capability associated with `key`, or populates it with `default` if
179    /// it is not present.
180    pub fn get_or_insert(&self, key: &Key, default: impl FnOnce() -> Capability) -> Capability {
181        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
182        match entries.get(key) {
183            Some(v) => v,
184            None => {
185                let v = (default)();
186                entries.insert(key.clone(), v.clone(), update_notifiers).unwrap();
187                v
188            }
189        }
190    }
191
192    /// Removes `key` from the entries, returning the capability at `key` if the key was already in
193    /// the entries.
194    pub fn remove(&self, key: &BorrowedKey) -> Option<Capability> {
195        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
196        entries.remove(key, update_notifiers)
197    }
198
199    /// Returns an iterator over a clone of the entries, sorted by key.
200    pub fn enumerate(&self) -> impl Iterator<Item = (Key, Capability)> + use<> {
201        self.lock()
202            .entries
203            .iter()
204            .map(|(k, v)| (k.clone(), v.clone()))
205            .collect::<Vec<_>>()
206            .into_iter()
207    }
208
209    /// Returns the keys.
210    pub fn snapshot_keys_as_strings(&self) -> Vec<String> {
211        let guard = self.lock();
212        match &guard.entries {
213            HybridMap::Vec(vec) => vec.iter().map(|(k, _)| k.to_string()).collect(),
214            HybridMap::Map(map) => map.keys().map(|k| k.to_string()).collect(),
215        }
216    }
217
218    /// Returns if entries is empty.
219    pub fn is_empty(&self) -> bool {
220        self.lock().entries.is_empty()
221    }
222
223    /// Removes all entries from the Dictionary and returns them as an iterator.
224    pub fn drain(&self) -> impl Iterator<Item = (Key, Capability)> + use<> {
225        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
226        let entries = std::mem::replace(entries, HybridMap::default());
227        for (key, _) in entries.iter() {
228            update_notifiers.update(EntryUpdate::Remove(&key))
229        }
230        entries.into_iter()
231    }
232
233    /// Creates a new Dictionary with entries cloned from this Dictionary.
234    ///
235    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
236    /// underlying data.
237    ///
238    /// If any value in the dictionary could not be cloned, returns an error.
239    pub fn shallow_copy(&self) -> Result<Self, ()> {
240        let copy = Self::new();
241        {
242            let DictInner { entries, .. } = &*self.lock();
243            let mut copy = copy.lock();
244            copy.entries = entries.shallow_copy()?;
245        }
246        Ok(copy)
247    }
248
249    /// Sends the name of an entry to the not found handler.
250    // Currently this is only used on target, but it's compatible with host.
251    #[allow(dead_code)]
252    pub(crate) fn not_found(&self, entry: &str) {
253        if let Some(not_found_handler) = &self.lock().not_found {
254            not_found_handler(entry);
255        }
256    }
257}
258
259impl DictInner {
260    #[cfg(target_os = "fuchsia")]
261    pub(crate) fn tasks(&mut self) -> &mut fasync::TaskGroup {
262        self.task_group.get_or_insert_with(|| fasync::TaskGroup::new())
263    }
264}
265
266pub(crate) const HYBRID_SWITCH_INSERTION_LEN: usize = 11;
267pub(crate) const HYBRID_SWITCH_REMOVAL_LEN: usize = 5;
268
269/// A map collection whose representation is a `Vec` for small `N` and a `BTreeMap` for larger `N`,
270/// where the threshold is defined by the constant `HYBRID_SWITCH_INSERTION_LEN` for insertion and
271/// `HYBRID_SWITCH_REMOVAL_LEN` for removal. This is a more space efficient representation for
272/// small `N` than a `BTreeMap`, which has a big impact because component_manager creates lots of
273/// `Dictionary` objects for component sandboxes.
274///
275/// Details: Rust's `BTreeMap` implementation uses a `B` value of 6, which means each node in the
276/// `BTreeMap` reserves space for 11 entries. Inserting 1 entry will allocate a node with space for
277/// 11 entries. Each entry is 48 bytes, 48 * 11 + some metadata = 544 bytes. Rounding up to the
278/// nearest scudo bucket size means each node consumes 656 bytes of memory.
279///
280/// A BTreeMap with only 2 entries uses 656 bytes when 112 would be sufficient (48 * 2 = 96 fits in
281/// the 112 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        capability: Capability,
316        update_notifiers: &mut UpdateNotifiers,
317    ) -> Result<(), fsandbox::CapabilityStoreError> {
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                    vec[index].1 = capability;
324                    Err(fsandbox::CapabilityStoreError::ItemAlreadyExists)
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                        Ok(())
333                    } else {
334                        vec.reserve_exact(1);
335                        vec.insert(index, (key, capability));
336                        Ok(())
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                    occupied.insert(capability);
345                    Err(fsandbox::CapabilityStoreError::ItemAlreadyExists)
346                }
347                Entry::Vacant(vacant) => {
348                    update_notifiers.update(EntryUpdate::Add(vacant.key(), &capability));
349                    vacant.insert(capability);
350                    Ok(())
351                }
352            },
353        }
354    }
355
356    pub fn remove(
357        &mut self,
358        key: &BorrowedKey,
359        update_notifiers: &mut UpdateNotifiers,
360    ) -> Option<Capability> {
361        let result = match self {
362            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, key) {
363                Ok(index) => {
364                    update_notifiers.update(EntryUpdate::Remove(key));
365                    Some(vec.remove(index).1)
366                }
367                Err(_) => None,
368            },
369            Self::Map(map) => {
370                let result = map.remove(key);
371                if result.is_some() {
372                    update_notifiers.update(EntryUpdate::Remove(key));
373                    if self.len() <= HYBRID_SWITCH_REMOVAL_LEN {
374                        self.switch_to_vec();
375                    }
376                }
377                result
378            }
379        };
380        result
381    }
382
383    pub fn append(
384        &mut self,
385        other: &Self,
386        update_notifiers: &mut UpdateNotifiers,
387    ) -> Result<(), ()> {
388        if other.is_empty() {
389            // If other is empty then return early.
390            return Ok(());
391        }
392
393        // If any clone would fail, throw an error early and don't modify the Dictionary
394        for (k, _) in other.iter() {
395            let contains_key = match self {
396                Self::Vec(vec) => matches!(Self::sorted_vec_index_of(vec, k), Ok(_)),
397                Self::Map(map) => map.contains_key(k),
398            };
399            if contains_key {
400                return Err(());
401            }
402        }
403
404        if self.len() + other.len() >= HYBRID_SWITCH_INSERTION_LEN {
405            // If at some point we will need to switch to a map then do it now.
406            self.switch_to_map();
407        } else if let Self::Vec(vec) = self {
408            // We're currently a Vec and won't need to convert to a Map so grow the Vec to the final
409            // size now.
410            vec.reserve(other.len());
411        }
412        for (k, v) in other.iter() {
413            self.insert(k.clone(), v.clone(), update_notifiers)
414                .expect("append: insert should have succeeded");
415        }
416        Ok(())
417    }
418
419    pub fn shallow_copy(&self) -> Result<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                Ok(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                Ok(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.