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}