shadow_shim_helper_rs/rootedcell/
mod.rs

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