@@ -51,6 +51,7 @@ use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
5151use reth_trie_sparse:: provider:: { RevealedNode , TrieNodeProvider , TrieNodeProviderFactory } ;
5252use std:: {
5353 sync:: {
54+ atomic:: { AtomicUsize , Ordering } ,
5455 mpsc:: { channel, Receiver , Sender } ,
5556 Arc ,
5657 } ,
@@ -116,6 +117,7 @@ fn storage_worker_loop<Factory>(
116117 task_ctx : ProofTaskCtx ,
117118 work_rx : CrossbeamReceiver < StorageWorkerJob > ,
118119 worker_id : usize ,
120+ available_workers : Arc < AtomicUsize > ,
119121 #[ cfg( feature = "metrics" ) ] metrics : ProofTaskTrieMetrics ,
120122) where
121123 Factory : DatabaseProviderFactory < Provider : BlockReader > ,
@@ -145,6 +147,12 @@ fn storage_worker_loop<Factory>(
145147 let mut storage_nodes_processed = 0u64 ;
146148
147149 while let Ok ( job) = work_rx. recv ( ) {
150+ // Reserve a worker slot with saturating decrement to prevent underflow.
151+ // Only decrement if counter is above zero.
152+ let decremented = available_workers
153+ . fetch_update ( Ordering :: Relaxed , Ordering :: Relaxed , |current| current. checked_sub ( 1 ) )
154+ . is_ok ( ) ;
155+
148156 match job {
149157 StorageWorkerJob :: StorageProof { input, result_sender } => {
150158 let hashed_address = input. hashed_address ;
@@ -186,6 +194,12 @@ fn storage_worker_loop<Factory>(
186194 total_processed = storage_proofs_processed,
187195 "Storage proof completed"
188196 ) ;
197+
198+ // Release worker slot only if we successfully reserved it.
199+ // Prevents counter from wrapping to usize::MAX on underflow.
200+ if decremented {
201+ available_workers. fetch_add ( 1 , Ordering :: Relaxed ) ;
202+ }
189203 }
190204
191205 StorageWorkerJob :: BlindedStorageNode { account, path, result_sender } => {
@@ -224,6 +238,12 @@ fn storage_worker_loop<Factory>(
224238 total_processed = storage_nodes_processed,
225239 "Blinded storage node completed"
226240 ) ;
241+
242+ // Release worker slot only if we successfully reserved it.
243+ // Prevents counter from wrapping to usize::MAX on underflow.
244+ if decremented {
245+ available_workers. fetch_add ( 1 , Ordering :: Relaxed ) ;
246+ }
227247 }
228248 }
229249 }
@@ -246,9 +266,11 @@ fn storage_worker_loop<Factory>(
246266///
247267/// Each worker:
248268/// 1. Receives `AccountWorkerJob` from crossbeam unbounded channel
249- /// 2. Computes result using its dedicated long-lived transaction
250- /// 3. Sends result directly to original caller via `std::mpsc`
251- /// 4. Repeats until channel closes (graceful shutdown)
269+ /// 2. Decrements availability counter to mark itself as busy
270+ /// 3. Computes result using its dedicated long-lived transaction
271+ /// 4. Sends result directly to original caller via `std::mpsc`
272+ /// 5. Increments availability counter to mark itself as available
273+ /// 6. Repeats until channel closes (graceful shutdown)
252274///
253275/// # Transaction Reuse
254276///
@@ -269,6 +291,7 @@ fn account_worker_loop<Factory>(
269291 work_rx : CrossbeamReceiver < AccountWorkerJob > ,
270292 storage_work_tx : CrossbeamSender < StorageWorkerJob > ,
271293 worker_id : usize ,
294+ available_workers : Arc < AtomicUsize > ,
272295 #[ cfg( feature = "metrics" ) ] metrics : ProofTaskTrieMetrics ,
273296) where
274297 Factory : DatabaseProviderFactory < Provider : BlockReader > ,
@@ -298,6 +321,12 @@ fn account_worker_loop<Factory>(
298321 let mut account_nodes_processed = 0u64 ;
299322
300323 while let Ok ( job) = work_rx. recv ( ) {
324+ // Reserve a worker slot with saturating decrement to prevent underflow.
325+ // Only decrement if counter is above zero.
326+ let decremented = available_workers
327+ . fetch_update ( Ordering :: Relaxed , Ordering :: Relaxed , |current| current. checked_sub ( 1 ) )
328+ . is_ok ( ) ;
329+
301330 match job {
302331 AccountWorkerJob :: AccountMultiproof { mut input, result_sender } => {
303332 let span = tracing:: debug_span!(
@@ -381,6 +410,12 @@ fn account_worker_loop<Factory>(
381410 "Account multiproof completed"
382411 ) ;
383412 drop ( _span_guard) ;
413+
414+ // Release worker slot only if we successfully reserved it.
415+ // Prevents counter from wrapping to usize::MAX on underflow.
416+ if decremented {
417+ available_workers. fetch_add ( 1 , Ordering :: Relaxed ) ;
418+ }
384419 }
385420
386421 AccountWorkerJob :: BlindedAccountNode { path, result_sender } => {
@@ -420,6 +455,12 @@ fn account_worker_loop<Factory>(
420455 "Blinded account node completed"
421456 ) ;
422457 drop ( _span_guard) ;
458+
459+ // Release worker slot only if we successfully reserved it.
460+ // Prevents counter from wrapping to usize::MAX on underflow.
461+ if decremented {
462+ available_workers. fetch_add ( 1 , Ordering :: Relaxed ) ;
463+ }
423464 }
424465 }
425466 }
@@ -866,6 +907,12 @@ pub struct ProofWorkerHandle {
866907 storage_work_tx : CrossbeamSender < StorageWorkerJob > ,
867908 /// Direct sender to account worker pool
868909 account_work_tx : CrossbeamSender < AccountWorkerJob > ,
910+ /// Counter tracking available storage workers. Workers decrement when starting work,
911+ /// increment when finishing. Used to determine whether to chunk multiproofs.
912+ storage_available_workers : Arc < AtomicUsize > ,
913+ /// Counter tracking available account workers. Workers decrement when starting work,
914+ /// increment when finishing. Used to determine whether to chunk multiproofs.
915+ account_available_workers : Arc < AtomicUsize > ,
869916}
870917
871918impl ProofWorkerHandle {
@@ -893,6 +940,10 @@ impl ProofWorkerHandle {
893940 let ( storage_work_tx, storage_work_rx) = unbounded :: < StorageWorkerJob > ( ) ;
894941 let ( account_work_tx, account_work_rx) = unbounded :: < AccountWorkerJob > ( ) ;
895942
943+ // Initialize availability counters
944+ let storage_available_workers = Arc :: new ( AtomicUsize :: new ( storage_worker_count) ) ;
945+ let account_available_workers = Arc :: new ( AtomicUsize :: new ( account_worker_count) ) ;
946+
896947 tracing:: debug!(
897948 target: "trie::proof_task" ,
898949 storage_worker_count,
@@ -910,6 +961,7 @@ impl ProofWorkerHandle {
910961 let view_clone = view. clone ( ) ;
911962 let task_ctx_clone = task_ctx. clone ( ) ;
912963 let work_rx_clone = storage_work_rx. clone ( ) ;
964+ let storage_available_workers_clone = storage_available_workers. clone ( ) ;
913965
914966 executor. spawn_blocking ( move || {
915967 #[ cfg( feature = "metrics" ) ]
@@ -921,6 +973,7 @@ impl ProofWorkerHandle {
921973 task_ctx_clone,
922974 work_rx_clone,
923975 worker_id,
976+ storage_available_workers_clone,
924977 #[ cfg( feature = "metrics" ) ]
925978 metrics,
926979 )
@@ -946,6 +999,7 @@ impl ProofWorkerHandle {
946999 let task_ctx_clone = task_ctx. clone ( ) ;
9471000 let work_rx_clone = account_work_rx. clone ( ) ;
9481001 let storage_work_tx_clone = storage_work_tx. clone ( ) ;
1002+ let account_available_workers_clone = account_available_workers. clone ( ) ;
9491003
9501004 executor. spawn_blocking ( move || {
9511005 #[ cfg( feature = "metrics" ) ]
@@ -958,6 +1012,7 @@ impl ProofWorkerHandle {
9581012 work_rx_clone,
9591013 storage_work_tx_clone,
9601014 worker_id,
1015+ account_available_workers_clone,
9611016 #[ cfg( feature = "metrics" ) ]
9621017 metrics,
9631018 )
@@ -972,7 +1027,12 @@ impl ProofWorkerHandle {
9721027
9731028 drop ( _guard) ;
9741029
975- Self :: new_handle ( storage_work_tx, account_work_tx)
1030+ Self :: new_handle (
1031+ storage_work_tx,
1032+ account_work_tx,
1033+ storage_available_workers,
1034+ account_available_workers,
1035+ )
9761036 }
9771037
9781038 /// Creates a new [`ProofWorkerHandle`] with direct access to worker pools.
@@ -981,8 +1041,25 @@ impl ProofWorkerHandle {
9811041 const fn new_handle (
9821042 storage_work_tx : CrossbeamSender < StorageWorkerJob > ,
9831043 account_work_tx : CrossbeamSender < AccountWorkerJob > ,
1044+ storage_available_workers : Arc < AtomicUsize > ,
1045+ account_available_workers : Arc < AtomicUsize > ,
9841046 ) -> Self {
985- Self { storage_work_tx, account_work_tx }
1047+ Self {
1048+ storage_work_tx,
1049+ account_work_tx,
1050+ storage_available_workers,
1051+ account_available_workers,
1052+ }
1053+ }
1054+
1055+ /// Returns true if there are available storage workers to process tasks.
1056+ pub fn has_available_storage_workers ( & self ) -> bool {
1057+ self . storage_available_workers . load ( Ordering :: Relaxed ) > 0
1058+ }
1059+
1060+ /// Returns true if there are available account workers to process tasks.
1061+ pub fn has_available_account_workers ( & self ) -> bool {
1062+ self . account_available_workers . load ( Ordering :: Relaxed ) > 0
9861063 }
9871064
9881065 /// Dispatch a storage proof computation to storage worker pool
0 commit comments