1use super::{Output, StorageJob, StorageWorker};
4use crate::{hashed_cursor::HashedStorageCursor, trie_cursor::TrieStorageCursor};
5use alloy_primitives::{B256, U256};
6use crossbeam_channel::{Receiver, Sender};
7use reth_execution_errors::StateRootError;
8use reth_storage_errors::db::DatabaseError;
9use std::{
10 panic::{catch_unwind, AssertUnwindSafe},
11 sync::Arc,
12 thread::{self, JoinHandle},
13};
14
15#[derive(Debug)]
17pub(super) struct StoragePipeline {
18 context: WorkerContext,
19 background: Option<JoinHandle<()>>,
20 announcements: Receiver<(B256, Receiver<B256>)>,
21 next: Option<(B256, Receiver<B256>)>,
22}
23
24impl StoragePipeline {
25 pub(super) fn spawn<L, T, H, TX>(
32 addresses: L,
33 workers: Vec<StorageWorker<T, H, TX>>,
34 context: WorkerContext,
35 storage_lookahead: usize,
36 ) -> Result<Self, DatabaseError>
37 where
38 L: Iterator<Item = Result<StorageJob, DatabaseError>> + Send + 'static,
39 T: TrieStorageCursor + Send + 'static,
40 H: HashedStorageCursor<Value = U256> + Send + 'static,
41 TX: Send + 'static,
42 {
43 assert!(!workers.is_empty(), "verification requires storage workers");
44 assert!(storage_lookahead > 0, "storage lookahead must be positive");
45 let (announcements_tx, announcements) = crossbeam_channel::bounded(storage_lookahead - 1);
48 let scheduler = StorageScheduler { addresses, workers, context: context.clone() };
49 let span = tracing::Span::current();
50 let background = thread::Builder::new()
51 .name("trie-lookahead".into())
52 .spawn(move || {
53 let _entered = span.enter();
54 scheduler.run(announcements_tx);
55 })
56 .map_err(|error| DatabaseError::Custom(Arc::new(error)))?;
57 Ok(Self { context, background: Some(background), announcements, next: None })
58 }
59
60 pub(super) fn next_root(&mut self, up_to: Option<B256>) -> Option<(B256, B256)> {
66 if self.context.is_cancelled() {
67 return None;
68 }
69 if self.next.is_none() {
70 self.next = crossbeam_channel::select! {
71 recv(self.announcements) -> announcement => announcement.ok(),
72 recv(self.context.cancel) -> _ => return None,
73 };
74 }
75 let Some((address, root)) = &self.next else { return None };
76 if up_to.is_some_and(|bound| *address > bound) {
77 return None;
78 }
79 let root = crossbeam_channel::select! {
80 recv(root) -> root => root,
81 recv(self.context.cancel) -> _ => return None,
82 };
83 match root {
84 Ok(root) => {
85 let address = *address;
86 self.next = None;
87 Some((address, root))
88 }
89 Err(_) => {
90 match self.context.cancel.recv() {
94 Ok(()) | Err(_) => None,
95 }
96 }
97 }
98 }
99
100 pub(super) fn finish(&mut self) {
102 self.join();
103 }
104
105 fn join(&mut self) {
106 if let Some(background) = self.background.take() &&
108 let Err(panic) = background.join() &&
109 !thread::panicking()
110 {
111 std::panic::resume_unwind(panic);
112 }
113 }
114}
115
116impl Drop for StoragePipeline {
117 fn drop(&mut self) {
118 self.join();
119 }
120}
121
122struct StorageScheduler<L, T, H, TX> {
125 addresses: L,
126 workers: Vec<StorageWorker<T, H, TX>>,
127 context: WorkerContext,
128}
129
130impl<L, T, H, TX> StorageScheduler<L, T, H, TX>
131where
132 L: Iterator<Item = Result<StorageJob, DatabaseError>>,
133 T: TrieStorageCursor + Send,
134 H: HashedStorageCursor<Value = U256> + Send,
135 TX: Send,
136{
137 fn run(mut self, announcements: Sender<(B256, Receiver<B256>)>) {
138 let context = self.context.clone();
139 let mut announcements = Some(announcements);
141 context.run(|| {
142 let worker_count = self.workers.len();
143 let pool = reth_tasks::WorkerPool::new(worker_count, "trie-verify");
144 let (jobs_tx, jobs_rx) = crossbeam_channel::bounded(worker_count * 2);
147 pool.in_place_scope(|scope| {
148 for worker in self.workers.drain(..) {
149 let jobs = jobs_rx.clone();
150 let context = self.context.clone();
151 let span = tracing::Span::current();
152 scope.spawn(move |_| {
153 let _entered = span.enter();
154 context.run(|| worker.run(jobs, &context));
155 });
156 }
157 drop(jobs_rx);
158 if let Some(sender) = &announcements {
162 context.run(|| self.produce(jobs_tx, sender));
163 }
164 drop(announcements.take());
165 });
166 Ok(())
167 });
168 }
169
170 fn produce(
171 &mut self,
172 jobs: Sender<(B256, Option<Sender<B256>>)>,
173 announcements: &Sender<(B256, Receiver<B256>)>,
174 ) -> Result<(), StateRootError> {
175 for job in self.addresses.by_ref() {
176 let job = job?;
177 let root = if job.root_required {
178 let (sender, receiver) = crossbeam_channel::bounded(1);
179 if !self.context.send(announcements, (job.address, receiver)) {
181 return Ok(());
182 }
183 Some(sender)
184 } else {
185 None
186 };
187 if !self.context.send(&jobs, (job.address, root)) {
188 return Ok(());
189 }
190 }
191 Ok(())
192 }
193}
194
195#[derive(Clone, Debug)]
199pub(super) struct WorkerContext {
200 events: Sender<Result<Output, StateRootError>>,
201 cancel: Receiver<()>,
202}
203
204impl WorkerContext {
205 pub(super) const fn new(
206 events: Sender<Result<Output, StateRootError>>,
207 cancel: Receiver<()>,
208 ) -> Self {
209 Self { events, cancel }
210 }
211
212 pub(super) fn send<T>(&self, sender: &Sender<T>, value: T) -> bool {
215 crossbeam_channel::select! {
216 send(sender, value) -> result => result.is_ok(),
217 recv(self.cancel) -> _ => false,
218 }
219 }
220
221 pub(super) fn receive<T>(&self, jobs: &Receiver<T>) -> Option<T> {
224 crossbeam_channel::select! {
225 recv(jobs) -> job => job.ok(),
226 recv(self.cancel) -> _ => None,
227 }
228 }
229
230 pub(super) fn is_cancelled(&self) -> bool {
232 self.cancel.try_recv() == Err(crossbeam_channel::TryRecvError::Disconnected)
233 }
234
235 pub(super) fn report_one(&self, output: Output) -> bool {
237 self.send(&self.events, Ok(output))
238 }
239
240 pub(super) fn run(&self, work: impl FnOnce() -> Result<(), StateRootError>) {
242 let result = catch_unwind(AssertUnwindSafe(work)).unwrap_or_else(|panic| {
243 let message = panic
244 .downcast_ref::<&str>()
245 .copied()
246 .or_else(|| panic.downcast_ref::<String>().map(String::as_str))
247 .unwrap_or("unknown panic");
248 Err(DatabaseError::Other(format!("trie verification thread panicked: {message}"))
249 .into())
250 });
251 if let Err(error) = result {
254 self.send(&self.events, Err(error));
255 }
256 }
257}