Skip to main content

turbo_tasks/
state.rs

1use std::{
2    fmt::Debug,
3    mem::take,
4    ops::{Deref, DerefMut},
5};
6
7use auto_hash_map::AutoSet;
8use bincode::{Decode, Encode};
9use parking_lot::{Mutex, MutexGuard};
10use tracing::trace_span;
11
12use crate::{
13    Invalidator, OperationValue, SerializationInvalidator, get_invalidator,
14    get_serialization_invalidator, manager::with_turbo_tasks,
15};
16
17#[derive(Encode, Decode)]
18struct StateInner<T> {
19    value: T,
20    invalidators: AutoSet<Invalidator>,
21}
22
23impl<T> StateInner<T> {
24    pub fn new(value: T) -> Self {
25        Self {
26            value,
27            invalidators: AutoSet::new(),
28        }
29    }
30
31    pub fn add_invalidator(&mut self, invalidator: Invalidator) {
32        self.invalidators.insert(invalidator);
33    }
34
35    /// Sets the value and returns the drained invalidators. The caller MUST
36    /// run them via [`run_invalidators`] *after* dropping the [`Mutex`] guard
37    /// — calling [`Invalidator::invalidate`] may grab locks in the backend which can lead to cycles
38    #[must_use]
39    fn set_unconditionally(&mut self, value: T) -> AutoSet<Invalidator> {
40        self.value = value;
41        take(&mut self.invalidators)
42    }
43
44    /// See [`Self::set_unconditionally`] for the locking contract on the
45    /// returned invalidators.
46    #[must_use]
47    fn update_conditionally(
48        &mut self,
49        update: impl FnOnce(&mut T) -> bool,
50    ) -> Option<AutoSet<Invalidator>> {
51        if !update(&mut self.value) {
52            return None;
53        }
54        Some(take(&mut self.invalidators))
55    }
56}
57
58impl<T: PartialEq> StateInner<T> {
59    /// See [`Self::set_unconditionally`] for the locking contract on the
60    /// returned invalidators.
61    #[must_use]
62    fn set(&mut self, value: T) -> Option<AutoSet<Invalidator>> {
63        if self.value == value {
64            return None;
65        }
66        self.value = value;
67        Some(take(&mut self.invalidators))
68    }
69}
70
71/// Notifies the backend that the [`State`] has been mutated: runs every
72/// dependent [`Invalidator`] and invalidates the serialized state. Must be
73/// called *outside* the [`StateInner`] mutex guard; see
74/// [`StateInner::set_unconditionally`] for why.
75///
76/// Both notifications resolve `TURBO_TASKS` from a task-local, so we do them
77/// inside a single [`with_turbo_tasks`] call to amortize that lookup.
78fn notify_mutated(
79    invalidators: AutoSet<Invalidator>,
80    serialization_invalidator: Option<&SerializationInvalidator>,
81) {
82    if invalidators.is_empty() && serialization_invalidator.is_none() {
83        return;
84    }
85    let _span = trace_span!("state value changed").entered();
86    with_turbo_tasks(|tt| {
87        Invalidator::invalidate_all(invalidators, &**tt);
88        if let Some(serialization_invalidator) = serialization_invalidator {
89            tt.invalidate_serialization(serialization_invalidator.task());
90        }
91    });
92}
93
94pub struct StateRef<'a, T> {
95    serialization_invalidator: Option<&'a SerializationInvalidator>,
96    // `Option` so `Drop` can `take()` the guard and release it before running
97    // invalidators. Always `Some` for the lifetime of the `StateRef` outside
98    // of `Drop`.
99    inner: Option<MutexGuard<'a, StateInner<T>>>,
100    mutated: bool,
101}
102
103impl<'a, T> StateRef<'a, T> {
104    fn new(
105        inner: MutexGuard<'a, StateInner<T>>,
106        serialization_invalidator: Option<&'a SerializationInvalidator>,
107    ) -> Self {
108        Self {
109            serialization_invalidator,
110            inner: Some(inner),
111            mutated: false,
112        }
113    }
114
115    fn inner(&self) -> &StateInner<T> {
116        self.inner.as_deref().expect("inner only None during Drop")
117    }
118
119    fn inner_mut(&mut self) -> &mut StateInner<T> {
120        self.inner
121            .as_deref_mut()
122            .expect("inner only None during Drop")
123    }
124}
125
126impl<T> Deref for StateRef<'_, T> {
127    type Target = T;
128
129    fn deref(&self) -> &Self::Target {
130        &self.inner().value
131    }
132}
133
134impl<T> DerefMut for StateRef<'_, T> {
135    fn deref_mut(&mut self) -> &mut Self::Target {
136        self.mutated = true;
137        &mut self.inner_mut().value
138    }
139}
140
141impl<T> Drop for StateRef<'_, T> {
142    fn drop(&mut self) {
143        if !self.mutated {
144            return;
145        }
146        // Drain invalidators while we still hold the guard, then drop the
147        // guard before running them. Running invalidators reaches into the
148        // backend and acquires task-storage shard locks, and the snapshot
149        // path takes the State mutex while holding such a shard lock — so
150        // running them under the guard is a lock-order inversion.
151        let mut guard = self.inner.take().expect("Drop only called once");
152        let invalidators = take(&mut guard.invalidators);
153        drop(guard);
154        notify_mutated(invalidators, self.serialization_invalidator);
155    }
156}
157
158pub mod parking_lot_mutex_bincode {
159    use bincode::{
160        BorrowDecode,
161        de::{BorrowDecoder, Decoder},
162        enc::Encoder,
163        error::{DecodeError, EncodeError},
164    };
165
166    use super::*;
167
168    pub fn encode<T: Encode, E: Encoder>(
169        mutex: &Mutex<T>,
170        encoder: &mut E,
171    ) -> Result<(), EncodeError> {
172        mutex.lock().encode(encoder)
173    }
174
175    pub fn decode<Context, T: Decode<Context>, D: Decoder<Context = Context>>(
176        decoder: &mut D,
177    ) -> Result<Mutex<T>, DecodeError> {
178        Ok(Mutex::new(T::decode(decoder)?))
179    }
180
181    pub fn borrow_decode<
182        'de,
183        Context,
184        T: BorrowDecode<'de, Context>,
185        D: BorrowDecoder<'de, Context = Context>,
186    >(
187        decoder: &mut D,
188    ) -> Result<Mutex<T>, DecodeError> {
189        Ok(Mutex::new(T::borrow_decode(decoder)?))
190    }
191}
192
193/// **This API violates core assumption of turbo-tasks, is believed to be unsound, and there's no
194/// plan fix it.** You should prefer to use [collectibles][crate::CollectiblesSource] instead of
195/// state where at all possible. This API may be removed in the future.
196///
197/// An [internally-mutable] type, similar to [`RefCell`][std::cell::RefCell] or [`Mutex`] that can
198/// be stored inside a [`VcValueType`].
199///
200/// **[`State`] should only be used with [`OperationVc`] and types that implement
201/// [`OperationValue`]**.
202///
203/// Setting values inside a [`State`] bypasses the normal argument and return value tracking
204/// that's tracks child function calls and re-runs tasks until their values settled. That system is
205/// needed for [strong consistency]. [`OperationVc`] ensures that function calls are reconnected
206/// with the parent/child call graph.
207///
208/// When reading a `State` with [`State::get`], the state itself (though not any values inside of
209/// it) is marked as a dependency of the current task.
210///
211/// [internally-mutable]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html
212/// [`VcValueType`]: crate::VcValueType
213/// [strong consistency]: crate::OperationVc::read_strongly_consistent
214/// [`OperationVc`]: crate::OperationVc
215/// [`OperationValue`]: crate::OperationValue
216#[derive(Encode, Decode)]
217pub struct State<T> {
218    serialization_invalidator: SerializationInvalidator,
219    #[bincode(with = "parking_lot_mutex_bincode")]
220    inner: Mutex<StateInner<T>>,
221}
222
223impl<T: Debug> Debug for State<T> {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        f.debug_struct("State")
226            .field("value", &self.inner.lock().value)
227            .finish()
228    }
229}
230
231impl<T: Default + OperationValue> Default for State<T> {
232    fn default() -> Self {
233        // Need to be explicit to ensure marking as stateful.
234        Self::new(Default::default())
235    }
236}
237
238impl<T> PartialEq for State<T> {
239    fn eq(&self, _other: &Self) -> bool {
240        false
241    }
242}
243impl<T> Eq for State<T> {}
244
245impl<T> State<T> {
246    pub fn new(value: T) -> Self
247    where
248        T: OperationValue,
249    {
250        Self {
251            serialization_invalidator: get_serialization_invalidator(),
252            inner: Mutex::new(StateInner::new(value)),
253        }
254    }
255
256    /// Gets the current value of the state. The current task will be registered
257    /// as dependency of the state and will be invalidated when the state
258    /// changes.
259    pub fn get(&self) -> StateRef<'_, T> {
260        let invalidator = get_invalidator();
261        let mut inner = self.inner.lock();
262        if let Some(invalidator) = invalidator {
263            inner.add_invalidator(invalidator);
264        }
265        StateRef::new(inner, Some(&self.serialization_invalidator))
266    }
267
268    /// Gets the current value of the state. Untracked.
269    pub fn get_untracked(&self) -> StateRef<'_, T> {
270        let inner = self.inner.lock();
271        StateRef::new(inner, Some(&self.serialization_invalidator))
272    }
273
274    /// Sets the current state without comparing it with the old value. This
275    /// should only be used if one is sure that the value has changed.
276    pub fn set_unconditionally(&self, value: T) {
277        let invalidators = {
278            let mut inner = self.inner.lock();
279            inner.set_unconditionally(value)
280        };
281        notify_mutated(invalidators, Some(&self.serialization_invalidator));
282    }
283
284    /// Updates the current state with the `update` function. The `update`
285    /// function need to return `true` when the value was modified. Exposing
286    /// the current value from the `update` function is not allowed and will
287    /// result in incorrect cache invalidation.
288    pub fn update_conditionally(&self, update: impl FnOnce(&mut T) -> bool) {
289        let Some(invalidators) = ({
290            let mut inner = self.inner.lock();
291            inner.update_conditionally(update)
292        }) else {
293            return;
294        };
295        notify_mutated(invalidators, Some(&self.serialization_invalidator));
296    }
297}
298
299impl<T: PartialEq> State<T> {
300    /// Update the current state when the `value` is different from the current
301    /// value. `T` must implement [PartialEq] for this to work.
302    pub fn set(&self, value: T) {
303        let Some(invalidators) = ({
304            let mut inner = self.inner.lock();
305            inner.set(value)
306        }) else {
307            return;
308        };
309        notify_mutated(invalidators, Some(&self.serialization_invalidator));
310    }
311}