token_cell/runtime_token_support.rs
1use std::fmt::Debug;
2
3/// A trait for types that can serve as unique identifiers for runtime tokens.
4///
5/// An `Identifier` must be able to be represented by an atomic type that
6/// can be incremented to produce the next identifier.
7pub trait Identifier: Eq + Debug + Copy + Send + Sync {
8 /// The atomic counterpart to this identifier type.
9 type AtomicType: RollingCounter<NonAtomic = Self>;
10 /// Whether or not comparison between two distinct token could yield `eq`.
11 type ComparisonMaySpuriouslyEq: crate::core::sealed::Boolean;
12}
13
14/// A trait for types that can be used as rolling counters. It is only intended for the atomic counterparts to [`Identifier`] types.
15pub trait RollingCounter {
16 /// The starting value of the rolling counter.
17 const DEFAULT: Self;
18 /// The non-atomic identifier type associated with this atomic type.
19 type NonAtomic;
20 /// Atomically increments the value and returns the previous value.
21 fn next(&self) -> Self::NonAtomic;
22}
23
24macro_rules! impl_identifier {
25 (comp u64) => {
26 crate::core::False
27 };
28 (comp u128) => {
29 crate::core::False
30 };
31 (comp $id: ty) => {
32 crate::core::False
33 };
34 ($id: ty, $atom: ty) => {
35 impl Identifier for $id {
36 type AtomicType = $atom;
37 type ComparisonMaySpuriouslyEq = impl_identifier!(comp $id);
38 }
39 impl RollingCounter for $atom {
40 const DEFAULT: Self = Self::new(0);
41 type NonAtomic = $id;
42 fn next(&self) -> Self::NonAtomic {
43 self.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
44 }
45 }
46 };
47}
48
49impl_identifier!(u8, portable_atomic::AtomicU8);
50impl_identifier!(u16, portable_atomic::AtomicU16);
51impl_identifier!(u32, portable_atomic::AtomicU32);
52impl_identifier!(u64, portable_atomic::AtomicU64);
53impl_identifier!(u128, portable_atomic::AtomicU128);
54
55/// The comparison error for runtime tokens.
56///
57/// Such an error being returned implies a massive bug in the calling code, as it implies that
58/// said code failed to track which instance of a [`runtime_token`] was used to construct a cell,
59/// and thus failed to ensure that the token was used correctly.
60///
61/// Unless the token is backed by an integer with sufficient size to guarantee that the program
62/// never constructs multiple tokens with a same identifier, your program SHOULD NOT rely on
63/// token identifiers being unique for soundness.
64#[derive(Debug, Clone, Copy)]
65pub struct IdMismatch<T: Identifier> {
66 /// The identifier of the token the cell was expecting.
67 pub cell: T,
68 /// The identifier of the token that was used to attempt accessing the cell's contents.
69 pub token: T,
70}
71
72impl<T: Identifier> ::core::fmt::Display for IdMismatch<T> {
73 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
74 write!(f, "{:?}", self)
75 }
76}
77
78/// Produces tokens that are also checked at runtime, ensuring that a [`TokenCell`] is never accidentally used with another instance of the same token type.
79///
80/// Each token of a given type generated by [`runtime_token`] being identified by a `u16` by default, constructing over 65536 instances may mean that distinct token instances share a same identifier,
81/// and may unlock each other's cells.
82///
83/// This means that if you accidentally use the wrong instance of a runtime token on a cell, you are likely but not _guaranteed_ to be protected by the identifier
84///
85/// You can chose the size of your tokens by calling `runtime_token!(pub MyToken: uX)`.
86/// `u64` has more overhead than the default `u16`, but guarantees that you will never accidentally use the wrong instance of a token undetected;
87/// therefore, runtime tokens that use `u64` as their backing allow you to use the [safe](crate::core::TokenCellTrait) access methods to [`TokenCell`]
88///
89/// [`TokenCell`]: crate::core::TokenCell
90#[macro_export]
91macro_rules! runtime_token {
92 ($(#[$meta: meta])* $vis: vis $id: ident) => {
93 runtime_token!($(#[meta])* $vis $id: u16);
94 };
95 ($(#[$meta: meta])* $vis: vis $id: ident: $ty: ty) => {
96 $crate::paste! {
97 $vis use [<__ $id _mod__ >]::$id;
98 #[allow(nonstandard_style)]
99 mod [<__ $id _mod__ >] {
100 use core::convert::Infallible;
101 use $crate::core::UnscopedToken;
102 use $crate::runtime_token_support::{Identifier, IdMismatch, RollingCounter};
103
104 static COUNTER: <$ty as Identifier>::AtomicType = <<$ty as Identifier>::AtomicType as RollingCounter>::DEFAULT;
105
106 $(#[$meta])*
107 #[derive(::core::fmt::Debug)]
108 pub struct $id($ty);
109
110 impl UnscopedToken for $id {
111 type ConstructionError = Infallible;
112 fn try_new() -> Result<Self, Self::ConstructionError> {
113 let id = COUNTER.next();
114 Ok(Self(id))
115 }
116 }
117 impl $crate::core::TokenTrait for $id {
118 type ComparisonMaySpuriouslyEq = $crate::core::False;
119 type RunError = Infallible;
120 type Identifier = $ty;
121 type ComparisonError = IdMismatch<$ty>;
122 type Branded<'a> = Self;
123 fn with_token<R, F: for<'a> FnOnce(Self::Branded<'a>) -> R>(f: F) -> Result<R, Self::RunError> {
124 Ok(f(Self::new()))
125 }
126 fn identifier(&self) -> Self::Identifier { self.0 }
127 fn compare(&self, id: &Self::Identifier) -> Result<(), Self::ComparisonError> {
128 if *id == self.0 { Ok(()) } else { Err(IdMismatch { cell: *id, token: self.0 }) }
129 }
130 }
131 }
132 }
133 };
134 ($($(#[$meta: meta])* $vis: vis $id: ident),*) => {
135 $($crate::runtime_token!($(#[meta])* $vis $id);)*
136 };
137 ($($(#[$meta: meta])* $vis: vis $id: ident: $ty: ty),*) => {
138 $($crate::runtime_token!($(#[meta])* $vis $id: $ty);)*
139 };
140}