shadow_shim_helper_rs/rootedcell/
mod.rs

1use core::{
2    marker::PhantomData,
3    sync::atomic::{AtomicU32, Ordering},
4};
5
6use once_cell::sync::OnceCell;
7use vasi::VirtualAddressSpaceIndependent;
8
9pub mod cell;
10// Uses allocation.
11#[cfg(feature = "alloc")]
12pub mod rc;
13pub mod refcell;
14
15/// Every object root is assigned a [Tag], which we ensure is globally unique.
16/// Each [Tag] value uniquely identifies a [Root].
17#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, VirtualAddressSpaceIndependent)]
18// Ensure consistent layout, since we use it in shared memory.
19#[repr(C)]
20pub struct Tag {
21    // Intended to be unique on a machine. We use a random number here.
22    global_id: TagGlobalId,
23    // Only unique within a process. We *could* just use global_id, and perhaps
24    // make it bigger, but having a local_id that we increment from 0 might be
25    // helpful for debugging.
26    local_id: TagLocalId,
27}
28
29/// Larger sizes here reduce the chance of collision, which could lead to
30/// silently missing bugs in some cases. Note though that there would both
31/// have to be a collision, and the code would need to incorrectly try to
32/// access data using the wrong root lock.
33///
34/// Increasing the size introduces some runtime overhead for storing, copying,
35/// and comparing tag values.
36type TagGlobalId = u32;
37
38/// Larger sizes here support a greater number of tags within a given prefix.
39///
40/// Increasing the size introduces some runtime overhead for storing, copying,
41/// and comparing tag values.
42type TagLocalId = u32;
43type TagLocallyUniquePartAtomicType = AtomicU32;
44
45impl Tag {
46    pub fn new() -> Self {
47        // Every instance of this module uses a random prefix for tags. This is
48        // primarily to handle the case where this module is used from multiple
49        // processes that share memory. We could alternatively use the pid here,
50        // but that may open us up to more corner cases that could cause
51        // collisions - e.g. pid namespaces, pid reuse, or multiple instances of
52        // this module ending up in a single process due to dependencies
53        // requiring different versions
54        // https://doc.rust-lang.org/cargo/reference/resolver.html#semver-compatibility.
55        static TAG_PREFIX: OnceCell<TagGlobalId> = OnceCell::new();
56        let prefix = *TAG_PREFIX.get_or_init(|| {
57            let mut buf = [0u8; size_of::<TagLocalId>()];
58            let n_initd =
59                rustix::rand::getrandom(&mut buf, rustix::rand::GetRandomFlags::empty()).unwrap();
60            assert_eq!(n_initd, size_of::<TagLocalId>());
61            TagLocalId::from_ne_bytes(buf)
62        });
63
64        static NEXT_TAG_SUFFIX: TagLocallyUniquePartAtomicType =
65            TagLocallyUniquePartAtomicType::new(0);
66        let suffix: TagLocalId = NEXT_TAG_SUFFIX.fetch_add(1, Ordering::Relaxed);
67
68        // Detect overflow
69        assert!(suffix != TagLocalId::MAX);
70
71        Self {
72            global_id: prefix,
73            local_id: suffix,
74        }
75    }
76}
77
78impl Default for Tag {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84/// A [Root] is a `![Sync]` token. Proof of access to a [Root] is used
85/// to inexpensively ensure safety of safety in [rc::RootedRc] and
86/// [refcell::RootedRefCell].
87#[derive(Debug, VirtualAddressSpaceIndependent)]
88// Ensure consistent layout, since this is an Archive type.
89#[repr(C)]
90pub struct Root {
91    tag: Tag,
92    _notsync: core::marker::PhantomData<core::cell::Cell<()>>,
93}
94
95impl Root {
96    pub fn new() -> Self {
97        let tag = Tag::new();
98        Self {
99            tag,
100            _notsync: PhantomData,
101        }
102    }
103
104    /// This root's globally unique tag.
105    fn tag(&self) -> Tag {
106        self.tag
107    }
108}
109
110impl Default for Root {
111    fn default() -> Self {
112        Self::new()
113    }
114}