Skip to main content

moirai_utils/result_cell/
state_word.rs

1//! The single atomic word the completion state machine synchronizes through.
2
3use core::sync::atomic::{AtomicU8, Ordering};
4
5use super::RESULT_PENDING;
6
7/// The one word the state machine synchronizes through.
8///
9/// The machine only ever loads, stores, and transitions a `u8`, which is all it
10/// needs; naming that as a trait keeps *where the word lives* a decision of the
11/// caller, not of the protocol. The async handle keeps it packed beside the
12/// result, because it allocates one cell per spawned task and refuses to pad
13/// each of them; the blocking handle puts it in an interference sector of its
14/// own, so the producer's publish does not invalidate the result's line. Both
15/// run the identical machine — see [the layout note](super#layout).
16///
17/// # Safety
18///
19/// An implementation must behave as one atomic `u8` cell:
20///
21/// - `load`, `store`, and `compare_exchange` are atomic and honor the ordering
22///   they are given (at least as strong as the requested one), because the
23///   cell publishes and consumes its result and waiter through those edges;
24/// - `load` returns only values a `store` or successful `compare_exchange`
25///   wrote, or [`pending`](Self::pending)'s initial value, and `get_mut`
26///   exposes that same cell.
27///
28/// A state word that reports `READY` before the result was written, or that
29/// drops the release/acquire edge, makes the cell read uninitialized memory.
30///
31/// Implementing it takes `unsafe impl`, so safe code cannot install a word that
32/// lies:
33///
34/// ```compile_fail,E0200
35/// use core::sync::atomic::Ordering;
36/// use moirai_utils::result_cell::StateWord;
37///
38/// struct Liar(u8);
39///
40/// impl StateWord for Liar {
41///     fn pending() -> Self { Liar(0) }
42///     fn load(&self, _: Ordering) -> u8 { 4 }
43///     fn store(&self, _: u8, _: Ordering) {}
44///     fn compare_exchange(&self, c: u8, _: u8, _: Ordering, _: Ordering) -> Result<u8, u8> { Ok(c) }
45///     fn get_mut(&mut self) -> &mut u8 { &mut self.0 }
46/// }
47/// ```
48pub unsafe trait StateWord: Send + Sync {
49    /// The word every cell starts at.
50    fn pending() -> Self;
51
52    /// Load the current state.
53    fn load(&self, order: Ordering) -> u8;
54
55    /// Store a new state.
56    fn store(&self, value: u8, order: Ordering);
57
58    /// Transition the state, returning the observed value on failure.
59    fn compare_exchange(
60        &self,
61        current: u8,
62        new: u8,
63        success: Ordering,
64        failure: Ordering,
65    ) -> Result<u8, u8>;
66
67    /// Exclusive access for `Drop`, which needs no atomicity.
68    fn get_mut(&mut self) -> &mut u8;
69}
70
71// SAFETY: `AtomicU8` is the atomic `u8` cell the contract describes, and each
72// method forwards the caller's ordering unchanged.
73unsafe impl StateWord for AtomicU8 {
74    #[inline]
75    fn pending() -> Self {
76        Self::new(RESULT_PENDING)
77    }
78
79    #[inline]
80    fn load(&self, order: Ordering) -> u8 {
81        Self::load(self, order)
82    }
83
84    #[inline]
85    fn store(&self, value: u8, order: Ordering) {
86        Self::store(self, value, order);
87    }
88
89    #[inline]
90    fn compare_exchange(
91        &self,
92        current: u8,
93        new: u8,
94        success: Ordering,
95        failure: Ordering,
96    ) -> Result<u8, u8> {
97        Self::compare_exchange(self, current, new, success, failure)
98    }
99
100    #[inline]
101    fn get_mut(&mut self) -> &mut u8 {
102        Self::get_mut(self)
103    }
104}
105
106// SAFETY: the wrapper only pads the `AtomicU8` it forwards every call to, with
107// the caller's ordering unchanged.
108unsafe impl StateWord for crate::cache::CacheAligned<AtomicU8> {
109    #[inline]
110    fn pending() -> Self {
111        Self::new(AtomicU8::new(RESULT_PENDING))
112    }
113
114    #[inline]
115    fn load(&self, order: Ordering) -> u8 {
116        self.0.load(order)
117    }
118
119    #[inline]
120    fn store(&self, value: u8, order: Ordering) {
121        self.0.store(value, order);
122    }
123
124    #[inline]
125    fn compare_exchange(
126        &self,
127        current: u8,
128        new: u8,
129        success: Ordering,
130        failure: Ordering,
131    ) -> Result<u8, u8> {
132        self.0.compare_exchange(current, new, success, failure)
133    }
134
135    #[inline]
136    fn get_mut(&mut self) -> &mut u8 {
137        self.0.get_mut()
138    }
139}