@@ -36,13 +36,12 @@ use ops::interface::{GenericNodeOrIndex, GenericTrieNode, GenericTrieUpdate};
36
36
use ops:: interface:: { GenericTrieValue , UpdatedNodeId } ;
37
37
use ops:: resharding:: { GenericTrieUpdateRetain , RetainMode } ;
38
38
pub use raw_node:: { Children , RawTrieNode , RawTrieNodeWithSize } ;
39
- use std:: cell:: RefCell ;
40
39
use std:: collections:: { BTreeMap , HashMap , HashSet } ;
41
40
use std:: fmt:: Write ;
42
41
use std:: hash:: Hash ;
43
42
use std:: ops:: DerefMut ;
44
43
use std:: str;
45
- use std:: sync:: { Arc , RwLock , RwLockReadGuard } ;
44
+ use std:: sync:: { Arc , Mutex , RwLock , RwLockReadGuard } ;
46
45
pub use trie_recording:: { SubtreeSize , TrieRecorder , TrieRecorderStats } ;
47
46
use trie_storage_update:: {
48
47
TrieStorageNodeWithSize , TrieStorageUpdate , UpdatedTrieStorageNodeWithSize ,
@@ -202,12 +201,13 @@ pub struct Trie {
202
201
/// (which can be toggled on the fly), trie nodes that have been looked up
203
202
/// once will be guaranteed to be cached, and further reads to these nodes
204
203
/// will encounter less gas cost.
205
- accounting_cache : RefCell < TrieAccountingCache > ,
204
+ accounting_cache : Mutex < TrieAccountingCache > ,
206
205
/// If present, we're capturing all trie nodes that have been accessed
207
206
/// during the lifetime of this Trie struct. This is used to produce a
208
207
/// state proof so that the same access pattern can be replayed using only
209
208
/// the captured result.
210
- recorder : Option < RefCell < TrieRecorder > > ,
209
+ // FIXME: make `TrieRecorder` internally MT-safe, instead of locking the entire structure.
210
+ recorder : Option < RwLock < TrieRecorder > > ,
211
211
/// If true, access to trie nodes (not values) charges gas and affects the
212
212
/// accounting cache. If false, access to trie nodes will not charge gas or
213
213
/// affect the accounting cache. Value accesses always charge gas no matter
@@ -544,11 +544,10 @@ impl Trie {
544
544
flat_storage_chunk_view : Option < FlatStorageChunkView > ,
545
545
) -> Self {
546
546
let accounting_cache = match storage. as_caching_storage ( ) {
547
- Some ( caching_storage) => RefCell :: new ( TrieAccountingCache :: new ( Some ( (
548
- caching_storage. shard_uid ,
549
- caching_storage. is_view ,
550
- ) ) ) ) ,
551
- None => RefCell :: new ( TrieAccountingCache :: new ( None ) ) ,
547
+ Some ( caching_storage) => {
548
+ TrieAccountingCache :: new ( Some ( ( caching_storage. shard_uid , caching_storage. is_view ) ) )
549
+ }
550
+ None => TrieAccountingCache :: new ( None ) ,
552
551
} ;
553
552
// Technically the charge_gas_for_trie_node_access should be set based
554
553
// on the flat storage protocol feature. When flat storage is enabled
@@ -562,7 +561,7 @@ impl Trie {
562
561
root,
563
562
charge_gas_for_trie_node_access,
564
563
flat_storage_chunk_view,
565
- accounting_cache,
564
+ accounting_cache : Mutex :: new ( accounting_cache ) ,
566
565
recorder : None ,
567
566
}
568
567
}
@@ -580,19 +579,19 @@ impl Trie {
580
579
/// Makes a new trie that has everything the same except that access
581
580
/// through that trie accumulates a state proof for all nodes accessed.
582
581
pub fn recording_reads_new_recorder ( & self ) -> Self {
583
- let recorder = RefCell :: new ( TrieRecorder :: new ( None ) ) ;
582
+ let recorder = RwLock :: new ( TrieRecorder :: new ( None ) ) ;
584
583
self . recording_reads_with_recorder ( recorder)
585
584
}
586
585
587
586
/// Makes a new trie that has everything the same except that access
588
587
/// through that trie accumulates a state proof for all nodes accessed.
589
588
/// We also supply a proof size limit to prevent the proof from growing too large.
590
589
pub fn recording_reads_with_proof_size_limit ( & self , proof_size_limit : usize ) -> Self {
591
- let recorder = RefCell :: new ( TrieRecorder :: new ( Some ( proof_size_limit) ) ) ;
590
+ let recorder = RwLock :: new ( TrieRecorder :: new ( Some ( proof_size_limit) ) ) ;
592
591
self . recording_reads_with_recorder ( recorder)
593
592
}
594
593
595
- pub fn recording_reads_with_recorder ( & self , recorder : RefCell < TrieRecorder > ) -> Self {
594
+ pub fn recording_reads_with_recorder ( & self , recorder : RwLock < TrieRecorder > ) -> Self {
596
595
let mut trie = Self :: new_with_memtries (
597
596
self . storage . clone ( ) ,
598
597
self . memtries . clone ( ) ,
@@ -605,20 +604,22 @@ impl Trie {
605
604
trie
606
605
}
607
606
608
- pub fn take_recorder ( self ) -> Option < RefCell < TrieRecorder > > {
607
+ pub fn take_recorder ( self ) -> Option < RwLock < TrieRecorder > > {
609
608
self . recorder
610
609
}
611
610
612
611
/// Takes the recorded state proof out of the trie.
613
612
pub fn recorded_storage ( & self ) -> Option < PartialStorage > {
614
- self . recorder . as_ref ( ) . map ( |recorder| recorder. borrow_mut ( ) . recorded_storage ( ) )
613
+ self . recorder
614
+ . as_ref ( )
615
+ . map ( |recorder| recorder. write ( ) . expect ( "no poison" ) . recorded_storage ( ) )
615
616
}
616
617
617
618
/// Returns the in-memory size of the recorded state proof. Useful for checking size limit of state witness
618
619
pub fn recorded_storage_size ( & self ) -> usize {
619
620
self . recorder
620
621
. as_ref ( )
621
- . map ( |recorder| recorder. borrow ( ) . recorded_storage_size ( ) )
622
+ . map ( |recorder| recorder. read ( ) . expect ( "no poison" ) . recorded_storage_size ( ) )
622
623
. unwrap_or_default ( )
623
624
}
624
625
@@ -627,14 +628,14 @@ impl Trie {
627
628
pub fn recorded_storage_size_upper_bound ( & self ) -> usize {
628
629
self . recorder
629
630
. as_ref ( )
630
- . map ( |recorder| recorder. borrow ( ) . recorded_storage_size_upper_bound ( ) )
631
+ . map ( |recorder| recorder. read ( ) . expect ( "no poison" ) . recorded_storage_size_upper_bound ( ) )
631
632
. unwrap_or_default ( )
632
633
}
633
634
634
635
pub fn check_proof_size_limit_exceed ( & self ) -> bool {
635
636
self . recorder
636
637
. as_ref ( )
637
- . map ( |recorder| recorder. borrow ( ) . check_proof_size_limit_exceed ( ) )
638
+ . map ( |recorder| recorder. read ( ) . expect ( "no poison" ) . check_proof_size_limit_exceed ( ) )
638
639
. unwrap_or_default ( )
639
640
}
640
641
@@ -662,7 +663,9 @@ impl Trie {
662
663
/// Get statistics about the recorded trie. Useful for observability and debugging.
663
664
/// This scans all of the recorded data, so could potentially be expensive to run.
664
665
pub fn recorder_stats ( & self ) -> Option < TrieRecorderStats > {
665
- self . recorder . as_ref ( ) . map ( |recorder| recorder. borrow ( ) . get_stats ( & self . root ) )
666
+ self . recorder
667
+ . as_ref ( )
668
+ . map ( |recorder| recorder. read ( ) . expect ( "no poison" ) . get_stats ( & self . root ) )
666
669
}
667
670
668
671
pub fn get_root ( & self ) -> & StateRoot {
@@ -683,7 +686,7 @@ impl Trie {
683
686
return ;
684
687
} ;
685
688
{
686
- let mut r = recorder. borrow_mut ( ) ;
689
+ let mut r = recorder. write ( ) . expect ( "no poison" ) ;
687
690
if r. codes_to_record . contains ( & account_id) {
688
691
return ;
689
692
}
@@ -695,7 +698,7 @@ impl Trie {
695
698
let key = TrieKey :: ContractCode { account_id } ;
696
699
let value_ref = self . get_optimized_ref ( & key. to_vec ( ) , KeyLookupMode :: FlatStorage ) ;
697
700
if let Ok ( Some ( value_ref) ) = value_ref {
698
- let mut r = recorder. borrow_mut ( ) ;
701
+ let mut r = recorder. write ( ) . expect ( "no poison" ) ;
699
702
r. record_code_len ( value_ref. len ( ) ) ;
700
703
}
701
704
}
@@ -711,7 +714,7 @@ impl Trie {
711
714
// that it is possible to generated continuous stream of witnesses with a fixed
712
715
// size. Using static key achieves that since in case of multiple receipts garbage
713
716
// data will simply be overwritten, not accumulated.
714
- recorder. borrow_mut ( ) . record_unaccounted (
717
+ recorder. write ( ) . expect ( "no poison" ) . record_unaccounted (
715
718
& CryptoHash :: hash_bytes ( b"__garbage_data_key_1720025071757228" ) ,
716
719
data. into ( ) ,
717
720
) ;
@@ -732,14 +735,15 @@ impl Trie {
732
735
) -> Result < Arc < [ u8 ] > , StorageError > {
733
736
let result = if side_effects && use_accounting_cache {
734
737
self . accounting_cache
735
- . borrow_mut ( )
738
+ . lock ( )
739
+ . unwrap ( )
736
740
. retrieve_raw_bytes_with_accounting ( hash, & * self . storage ) ?
737
741
} else {
738
742
self . storage . retrieve_raw_bytes ( hash) ?
739
743
} ;
740
744
if side_effects {
741
745
if let Some ( recorder) = & self . recorder {
742
- recorder. borrow_mut ( ) . record ( hash, result. clone ( ) ) ;
746
+ recorder. write ( ) . expect ( "no poison" ) . record ( hash, result. clone ( ) ) ;
743
747
}
744
748
}
745
749
Ok ( result)
@@ -1291,13 +1295,14 @@ impl Trie {
1291
1295
if charge_gas_for_trie_node_access {
1292
1296
for ( node_hash, serialized_node) in & accessed_nodes {
1293
1297
self . accounting_cache
1294
- . borrow_mut ( )
1298
+ . lock ( )
1299
+ . unwrap ( )
1295
1300
. retroactively_account ( * node_hash, serialized_node. clone ( ) ) ;
1296
1301
}
1297
1302
}
1298
1303
if let Some ( recorder) = & self . recorder {
1299
1304
for ( node_hash, serialized_node) in accessed_nodes {
1300
- recorder. borrow_mut ( ) . record ( & node_hash, serialized_node) ;
1305
+ recorder. write ( ) . expect ( "no poison" ) . record ( & node_hash, serialized_node) ;
1301
1306
}
1302
1307
}
1303
1308
mem_value
@@ -1491,10 +1496,11 @@ impl Trie {
1491
1496
let value_hash = hash ( value) ;
1492
1497
let arc_value: Arc < [ u8 ] > = value. clone ( ) . into ( ) ;
1493
1498
self . accounting_cache
1494
- . borrow_mut ( )
1499
+ . lock ( )
1500
+ . unwrap ( )
1495
1501
. retroactively_account ( value_hash, arc_value. clone ( ) ) ;
1496
1502
if let Some ( recorder) = & self . recorder {
1497
- recorder. borrow_mut ( ) . record ( & value_hash, arc_value) ;
1503
+ recorder. write ( ) . expect ( "no poison" ) . record ( & value_hash, arc_value) ;
1498
1504
}
1499
1505
Ok ( value. clone ( ) )
1500
1506
}
@@ -1515,7 +1521,7 @@ impl Trie {
1515
1521
{
1516
1522
// Call `get` for contract codes requested to be recorded.
1517
1523
let codes_to_record = if let Some ( recorder) = & self . recorder {
1518
- recorder. borrow ( ) . codes_to_record . clone ( )
1524
+ recorder. read ( ) . expect ( "no poison" ) . codes_to_record . clone ( )
1519
1525
} else {
1520
1526
HashSet :: default ( )
1521
1527
} ;
@@ -1537,7 +1543,8 @@ impl Trie {
1537
1543
{
1538
1544
// Get trie_update for memtrie
1539
1545
let guard = self . memtries . as_ref ( ) . unwrap ( ) . read ( ) . unwrap ( ) ;
1540
- let mut recorder = self . recorder . as_ref ( ) . map ( |recorder| recorder. borrow_mut ( ) ) ;
1546
+ let mut recorder =
1547
+ self . recorder . as_ref ( ) . map ( |recorder| recorder. write ( ) . expect ( "no poison" ) ) ;
1541
1548
let tracking_mode = match & mut recorder {
1542
1549
Some ( recorder) => TrackingMode :: RefcountsAndAccesses ( recorder. deref_mut ( ) ) ,
1543
1550
None => TrackingMode :: Refcounts ,
@@ -1650,7 +1657,7 @@ impl Trie {
1650
1657
}
1651
1658
1652
1659
pub fn get_trie_nodes_count ( & self ) -> TrieNodesCount {
1653
- self . accounting_cache . borrow ( ) . get_trie_nodes_count ( )
1660
+ self . accounting_cache . lock ( ) . unwrap ( ) . get_trie_nodes_count ( )
1654
1661
}
1655
1662
1656
1663
/// Splits the trie, separating entries by the boundary account.
0 commit comments