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}