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