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(Deserialize, 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
166// Preserve transparent serde and the derived NonZero<u32> bincode format, but reject references
167// to transient tasks. RawVc packs TaskId into its own integer and checks that path separately.
168impl Serialize for TaskId {
169    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
170        debug_assert!(
171            !self.is_transient(),
172            "transient TaskId must not be serialized"
173        );
174        self.id.serialize(serializer)
175    }
176}
177
178impl Encode for TaskId {
179    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
180        debug_assert!(!self.is_transient(), "transient TaskId must not be encoded");
181        self.id.encode(encoder)
182    }
183}
184
185impl Debug for TaskId {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        f.debug_struct("TaskId").field("id", &self.id).finish()
188    }
189}
190
191unsafe impl crate::NonLocalValue for TaskId {}
192
193/// `TaskId` values are constrained to 31 bits to preserve a niche for [`crate::RawVc`]. Bit 30
194/// marks transient tasks; bit 31 is always zero.
195pub const TRANSIENT_TASK_BIT: u32 = 0x4000_0000;
196
197/// The largest value a [`TaskId`] may hold (31 bits set).
198pub const TASK_ID_MAX: u32 = 0x7FFF_FFFF;
199
200impl TaskId {
201    pub fn is_transient(&self) -> bool {
202        **self & TRANSIENT_TASK_BIT != 0
203    }
204    pub fn persistence(&self) -> TaskPersistence {
205        // tasks with `TaskPersistence::LocalCells` have no `TaskId`, so we can ignore that case
206        if self.is_transient() {
207            TaskPersistence::Transient
208        } else {
209            TaskPersistence::Persistent
210        }
211    }
212}
213
214macro_rules! make_registered_serializable {
215    ($ty:ty, $primitive:ty, $get_object:path, $validate_type_id:path $(,)?) => {
216        impl Serialize for $ty {
217            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
218            where
219                S: serde::Serializer,
220            {
221                serializer.serialize_u16(self.id.into())
222            }
223        }
224
225        impl<'de> Deserialize<'de> for $ty {
226            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
227            where
228                D: serde::Deserializer<'de>,
229            {
230                struct DeserializeVisitor;
231                impl<'de> Visitor<'de> for DeserializeVisitor {
232                    type Value = $ty;
233
234                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
235                        formatter.write_str(concat!("an id of a registered ", stringify!($ty)))
236                    }
237
238                    fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
239                    where
240                        E: serde::de::Error,
241                    {
242                        match Self::Value::new(v) {
243                            Some(value) => {
244                                if let Some(error) = $validate_type_id(value) {
245                                    Err(E::custom(error))
246                                } else {
247                                    Ok(value)
248                                }
249                            }
250                            None => Err(E::unknown_variant(&format!("{v}"), &["a non zero u16"])),
251                        }
252                    }
253                }
254
255                deserializer.deserialize_u16(DeserializeVisitor)
256            }
257        }
258
259        impl Debug for $ty {
260            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261                f.debug_struct(stringify!($ty))
262                    .field("id", &self.id)
263                    .field("name", &$get_object(*self))
264                    .finish()
265            }
266        }
267
268        impl Encode for $ty {
269            fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
270                <NonZero<$primitive> as Encode>::encode(&self.id, encoder)
271            }
272        }
273
274        impl<Context> Decode<Context> for $ty {
275            fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
276                let value = Self {
277                    id: NonZero::<$primitive>::decode(decoder)?,
278                };
279                if let Some(error) = $validate_type_id(value) {
280                    Err(DecodeError::OtherString(error.to_string()))
281                } else {
282                    Ok(value)
283                }
284            }
285        }
286
287        impl_borrow_decode!($ty);
288    };
289}
290
291make_registered_serializable!(
292    ValueTypeId,
293    u16,
294    registry::get_value_type,
295    registry::validate_value_type_id,
296);
297make_registered_serializable!(
298    TraitTypeId,
299    u16,
300    registry::get_trait,
301    registry::validate_trait_type_id,
302);
303make_registered_serializable!(
304    FunctionId,
305    u16,
306    registry::get_native_function,
307    registry::validate_function_id,
308);