Skip to main content

turbo_tasks/
id.rs

1use std::{
2    fmt::{Debug, Display},
3    mem::transmute_copy,
4    num::{NonZero, NonZeroU64, TryFromIntError},
5    ops::Deref,
6};
7
8use bincode::{
9    Decode, Encode,
10    de::Decoder,
11    enc::Encoder,
12    error::{DecodeError, EncodeError},
13    impl_borrow_decode,
14};
15use serde::{Deserialize, Serialize, de::Visitor};
16
17use crate::{TaskPersistence, registry};
18
19macro_rules! define_id {
20    (
21        $name:ident : $primitive:ty
22        $(,max = $max:expr)?
23        $(,derive($($derive:ty),*))?
24        $(,serde($serde:tt))?
25        $(,doc = $doc:literal)*
26        $(,)?
27    ) => {
28        $(#[doc = $doc])*
29        #[derive(Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord $($(,$derive)*)? )]
30        $(#[serde($serde)])?
31        pub struct $name {
32            id: NonZero<$primitive>,
33        }
34
35        impl $name {
36            pub const MIN: Self = Self { id: NonZero::<$primitive>::MIN };
37            // `max` defaults to the primitive's max; types packed into a smaller
38            // bit field (e.g. `TaskId` in `RawVc`) override it.
39            pub const MAX: Self = {
40                let _max: $primitive = NonZero::<$primitive>::MAX.get();
41                $( let _max: $primitive = $max; )?
42                // SAFETY: `_max` is either `NonZero::MAX` or a caller-provided
43                // positive constant; both are non-zero.
44                Self { id: unsafe { NonZero::<$primitive>::new_unchecked(_max) } }
45            };
46
47            /// Constructs a wrapper type from the numeric identifier.
48            ///
49            /// # Safety
50            ///
51            /// The passed `id` must not be zero.
52            pub const unsafe fn new_unchecked(id: $primitive) -> Self {
53                Self { id: unsafe { NonZero::<$primitive>::new_unchecked(id) } }
54            }
55            /// Constructs a wrapper type from the numeric identifier.
56            ///
57            /// Returns `None` if the provided `id` is zero, otherwise returns
58            /// `Some(Self)` containing the wrapped non-zero identifier.
59            pub fn new(id: $primitive) -> Option<Self> {
60                NonZero::<$primitive>::new(id).map(|id| Self{id})
61            }
62            /// Allows `const` conversion to a [`NonZeroU64`], useful with
63            /// [`crate::id_factory::IdFactory::new_const`].
64            pub const fn to_non_zero_u64(self) -> NonZeroU64 {
65                const {
66                    assert!(<$primitive>::BITS <= u64::BITS);
67                }
68                unsafe { NonZeroU64::new_unchecked(self.id.get() as u64) }
69            }
70            /// Allows `const` conversion to [`NonZero<$primitive>`]
71            pub const fn to_non_zero_primitive(self) -> NonZero<$primitive> {
72                self.id
73            }
74            pub const fn to_primitive(self) -> $primitive {
75                self.id.get()
76            }
77        }
78
79        impl Display for $name {
80            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81                write!(f, concat!(stringify!($name), " {}"), self.id)
82            }
83        }
84
85        impl Deref for $name {
86            type Target = $primitive;
87
88            fn deref(&self) -> &Self::Target {
89                // SAFETY: `NonZero<T>` is guaranteed to have the same layout as `T`
90                unsafe { transmute_copy(&&self.id) }
91            }
92        }
93
94        define_id!(@impl_try_from_primitive_conversion $name $primitive);
95
96        impl From<NonZero<$primitive>> for $name {
97            fn from(id: NonZero::<$primitive>) -> Self {
98                Self {
99                    id,
100                }
101            }
102        }
103
104        impl From<$name> for NonZeroU64 {
105            fn from(id: $name) -> Self {
106                id.to_non_zero_u64()
107            }
108        }
109    };
110    (
111        @impl_try_from_primitive_conversion $name:ident u64
112    ) => {
113        // we get a `TryFrom` blanket impl for free via the `From` impl
114    };
115    (
116        @impl_try_from_primitive_conversion $name:ident $primitive:ty
117    ) => {
118        impl TryFrom<$primitive> for $name {
119            type Error = TryFromIntError;
120
121            fn try_from(id: $primitive) -> Result<Self, Self::Error> {
122                Ok(Self {
123                    id: NonZero::try_from(id)?
124                })
125            }
126        }
127
128        impl TryFrom<NonZeroU64> for $name {
129            type Error = TryFromIntError;
130
131            fn try_from(id: NonZeroU64) -> Result<Self, Self::Error> {
132                Ok(Self { id: NonZero::try_from(id)? })
133            }
134        }
135    };
136}
137
138define_id!(
139    TaskId: u32,
140    // Capped below `u32::MAX` so the id fits in 31 bits when packed into `RawVc`.
141    max = TASK_ID_MAX,
142    derive(Serialize, Deserialize, Encode, Decode),
143    serde(transparent),
144);
145define_id!(
146    ValueTypeId: u16,
147    // Capped below `u16::MAX` so the id fits in 10 bits when packed into `CellId`.
148    max = crate::CellId::MAX_VALUE_TYPE_ID,
149);
150define_id!(FunctionId: u16);
151define_id!(TraitTypeId: u16);
152define_id!(
153    LocalTaskId: u32,
154    derive(Debug, Serialize, Deserialize, Encode, Decode),
155    serde(transparent),
156    doc = "Represents the nth `local` function call inside a task.",
157);
158define_id!(
159    ExecutionId: u16,
160    derive(Debug, Serialize, Deserialize, Encode, Decode),
161    serde(transparent),
162    doc = "An identifier for a specific task execution. Used to assert that local `Vc`s don't \
163        leak. This value may overflow and re-use old values.",
164);
165
166impl Debug for TaskId {
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        f.debug_struct("TaskId").field("id", &self.id).finish()
169    }
170}
171
172unsafe impl crate::NonLocalValue for TaskId {}
173
174/// `TaskId` values are constrained to 31 bits to preserve a niche for [`crate::RawVc`]. Bit 30
175/// marks transient tasks; bit 31 is always zero.
176pub const TRANSIENT_TASK_BIT: u32 = 0x4000_0000;
177
178/// The largest value a [`TaskId`] may hold (31 bits set).
179pub const TASK_ID_MAX: u32 = 0x7FFF_FFFF;
180
181impl TaskId {
182    pub fn is_transient(&self) -> bool {
183        **self & TRANSIENT_TASK_BIT != 0
184    }
185    pub fn persistence(&self) -> TaskPersistence {
186        // tasks with `TaskPersistence::LocalCells` have no `TaskId`, so we can ignore that case
187        if self.is_transient() {
188            TaskPersistence::Transient
189        } else {
190            TaskPersistence::Persistent
191        }
192    }
193}
194
195macro_rules! make_registered_serializable {
196    ($ty:ty, $primitive:ty, $get_object:path, $validate_type_id:path $(,)?) => {
197        impl Serialize for $ty {
198            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
199            where
200                S: serde::Serializer,
201            {
202                serializer.serialize_u16(self.id.into())
203            }
204        }
205
206        impl<'de> Deserialize<'de> for $ty {
207            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
208            where
209                D: serde::Deserializer<'de>,
210            {
211                struct DeserializeVisitor;
212                impl<'de> Visitor<'de> for DeserializeVisitor {
213                    type Value = $ty;
214
215                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
216                        formatter.write_str(concat!("an id of a registered ", stringify!($ty)))
217                    }
218
219                    fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
220                    where
221                        E: serde::de::Error,
222                    {
223                        match Self::Value::new(v) {
224                            Some(value) => {
225                                if let Some(error) = $validate_type_id(value) {
226                                    Err(E::custom(error))
227                                } else {
228                                    Ok(value)
229                                }
230                            }
231                            None => Err(E::unknown_variant(&format!("{v}"), &["a non zero u16"])),
232                        }
233                    }
234                }
235
236                deserializer.deserialize_u16(DeserializeVisitor)
237            }
238        }
239
240        impl Debug for $ty {
241            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242                f.debug_struct(stringify!($ty))
243                    .field("id", &self.id)
244                    .field("name", &$get_object(*self))
245                    .finish()
246            }
247        }
248
249        impl Encode for $ty {
250            fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
251                <NonZero<$primitive> as Encode>::encode(&self.id, encoder)
252            }
253        }
254
255        impl<Context> Decode<Context> for $ty {
256            fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
257                let value = Self {
258                    id: NonZero::<$primitive>::decode(decoder)?,
259                };
260                if let Some(error) = $validate_type_id(value) {
261                    Err(DecodeError::OtherString(error.to_string()))
262                } else {
263                    Ok(value)
264                }
265            }
266        }
267
268        impl_borrow_decode!($ty);
269    };
270}
271
272make_registered_serializable!(
273    ValueTypeId,
274    u16,
275    registry::get_value_type,
276    registry::validate_value_type_id,
277);
278make_registered_serializable!(
279    TraitTypeId,
280    u16,
281    registry::get_trait,
282    registry::validate_trait_type_id,
283);
284make_registered_serializable!(
285    FunctionId,
286    u16,
287    registry::get_native_function,
288    registry::validate_function_id,
289);