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}