reth_trie_common/
lazy_hashed_state.rs1use crate::{HashedPostState, HashedPostStateSorted};
4use alloc::sync::Arc;
5use core::fmt;
6use reth_primitives_traits::sync::OnceLock;
7
8#[derive(Clone)]
11pub struct LazyHashedPostStateSorted {
12 value: Arc<OnceLock<Arc<HashedPostStateSorted>>>,
13}
14
15impl LazyHashedPostStateSorted {
16 pub fn ready(state: Arc<HashedPostStateSorted>) -> Self {
18 Self { value: Arc::new(OnceLock::from(state)) }
19 }
20
21 #[cfg(feature = "std")]
23 pub fn pending(hashed_state: Arc<HashedPostState>) -> (Self, HashedPostStateSortedProducer) {
24 let value = Arc::new(OnceLock::new());
25 (Self { value: Arc::clone(&value) }, HashedPostStateSortedProducer { value, hashed_state })
26 }
27
28 pub fn get(&self) -> &Arc<HashedPostStateSorted> {
30 #[cfg(feature = "std")]
31 {
32 self.value.wait()
33 }
34 #[cfg(not(feature = "std"))]
35 {
36 self.value.get().expect("hashed state must be initialized")
37 }
38 }
39}
40
41impl fmt::Debug for LazyHashedPostStateSorted {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.debug_struct("LazyHashedPostStateSorted")
44 .field("initialized", &self.value.get().is_some())
45 .finish()
46 }
47}
48
49impl PartialEq for LazyHashedPostStateSorted {
50 fn eq(&self, other: &Self) -> bool {
51 self.get() == other.get()
52 }
53}
54
55impl Eq for LazyHashedPostStateSorted {}
56
57#[cfg(feature = "serde")]
58impl serde::Serialize for LazyHashedPostStateSorted {
59 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60 where
61 S: serde::Serializer,
62 {
63 self.get().serialize(serializer)
64 }
65}
66
67#[cfg(feature = "serde")]
68impl<'de> serde::Deserialize<'de> for LazyHashedPostStateSorted {
69 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
70 where
71 D: serde::Deserializer<'de>,
72 {
73 Arc::deserialize(deserializer).map(Self::ready)
74 }
75}
76
77#[derive(Debug)]
79#[must_use = "call compute_and_publish to wake hashed state waiters"]
80pub struct HashedPostStateSortedProducer {
81 value: Arc<OnceLock<Arc<HashedPostStateSorted>>>,
82 hashed_state: Arc<HashedPostState>,
83}
84
85impl HashedPostStateSortedProducer {
86 pub fn compute_and_publish(self) -> Arc<HashedPostStateSorted> {
88 let sorted = Arc::new(match Arc::try_unwrap(self.hashed_state) {
89 Ok(state) => state.into_sorted(),
90 Err(state) => state.clone_into_sorted(),
91 });
92 let _ = self.value.set(Arc::clone(&sorted));
93 sorted
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use crate::HashedStorage;
100
101 use super::*;
102 use alloy_primitives::{map::B256Map, B256, U256};
103 use reth_primitives_traits::Account;
104 use std::{
105 thread,
106 time::{Duration, Instant},
107 };
108
109 fn empty_pending() -> (LazyHashedPostStateSorted, HashedPostStateSortedProducer) {
110 LazyHashedPostStateSorted::pending(Arc::new(HashedPostState::default()))
111 }
112
113 #[test]
114 fn test_lazy_ready_is_initialized() {
115 let lazy = LazyHashedPostStateSorted::ready(Arc::default());
116 let _ = lazy.get();
117 }
118
119 #[test]
120 fn test_lazy_clone_shares_state() {
121 let lazy1 = LazyHashedPostStateSorted::ready(Arc::default());
122 let lazy2 = lazy1.clone();
123
124 assert!(Arc::ptr_eq(lazy1.get(), lazy2.get()));
126 }
127
128 #[test]
129 fn ready_returns_immediately() {
130 let bundle = Arc::new(HashedPostStateSorted::default());
131 let deferred = LazyHashedPostStateSorted::ready(bundle.clone());
132
133 let result = deferred.get();
134
135 assert_eq!(result.total_len(), bundle.total_len());
136 }
137
138 #[test]
139 fn pending_waits_for_task_and_caches_result() {
140 let (deferred, task) = empty_pending();
141 let published = task.compute_and_publish();
142 let first = deferred.get();
143 let second = deferred.get();
144
145 assert!(Arc::ptr_eq(&published, first));
146 assert!(Arc::ptr_eq(first, second));
147 }
148
149 #[test]
150 fn pending_wait_blocks_until_task_publishes() {
151 let (deferred, task) = empty_pending();
152
153 let handle = thread::spawn(move || deferred.get().clone());
154 thread::sleep(Duration::from_millis(20));
155 assert!(!handle.is_finished());
156
157 let published = task.compute_and_publish();
158 let result = handle.join().unwrap();
159
160 assert!(Arc::ptr_eq(&published, &result));
161 }
162
163 #[test]
164 fn concurrent_waits_share_published_result() {
165 let (deferred, task) = empty_pending();
166 let deferred2 = deferred.clone();
167
168 let handle = thread::spawn(move || deferred2.get().clone());
169 let published = task.compute_and_publish();
170 let result1 = deferred.get().clone();
171 let result2 = handle.join().unwrap();
172
173 assert!(Arc::ptr_eq(&published, &result1));
174 assert!(Arc::ptr_eq(&result1, &result2));
175 }
176
177 #[test]
178 fn sorts_non_empty_inputs() {
179 let hashed_address = B256::with_last_byte(1);
180 let hashed_slot = B256::with_last_byte(2);
181 let hashed_state = HashedPostState::default()
182 .with_accounts([(hashed_address, Some(Account::default()))])
183 .with_storages([(
184 hashed_address,
185 HashedStorage::from_iter([(hashed_slot, U256::from(1))]),
186 )]);
187
188 let (deferred, task) = LazyHashedPostStateSorted::pending(Arc::new(hashed_state));
189 let _ = task.compute_and_publish();
190 let result = deferred.get().clone();
191
192 assert_eq!(result.total_len(), 2);
193 }
194
195 #[test]
196 fn wait_does_not_block_after_first_compute() {
197 let mut accounts = B256Map::default();
198 for i in 0..100 {
199 accounts.insert(B256::with_last_byte(i), Some(Account::default()));
200 }
201 let (deferred, task) = LazyHashedPostStateSorted::pending(Arc::new(HashedPostState {
202 accounts,
203 storages: Default::default(),
204 }));
205
206 let _ = task.compute_and_publish();
207 let _ = deferred.get().clone();
208 let start = Instant::now();
209 let _ = deferred.get().clone();
210
211 assert!(start.elapsed() < Duration::from_millis(10));
212 }
213}