diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index d81bb605f2..a137a39705 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -810,12 +810,13 @@ impl Bank { // TODO: put this assert back in // assert!(!self.is_frozen()); inc_new_counter_debug!("bank-register_tick-registered", 1); + // Grab blockhash lock before incrementing tick height so that replay stage does + // not attempt to freeze after observing the last tick and before blockhash is + // updated + let mut w_blockhash_queue = self.blockhash_queue.write().unwrap(); let current_tick_height = self.tick_height.fetch_add(1, Ordering::Relaxed) as u64; if self.is_block_boundary(current_tick_height + 1) { - self.blockhash_queue - .write() - .unwrap() - .register_hash(hash, &self.fee_calculator); + w_blockhash_queue.register_hash(hash, &self.fee_calculator); } } diff --git a/runtime/tests/bank.rs b/runtime/tests/bank.rs new file mode 100644 index 0000000000..a466bb9d04 --- /dev/null +++ b/runtime/tests/bank.rs @@ -0,0 +1,41 @@ +use solana_runtime::bank::Bank; +use solana_sdk::genesis_block::create_genesis_block; +use solana_sdk::hash::hash; +use solana_sdk::pubkey::Pubkey; +use std::sync::Arc; +use std::thread::Builder; + +#[test] +fn test_race_register_tick_freeze() { + solana_logger::setup(); + + let (mut genesis_block, _) = create_genesis_block(50); + genesis_block.ticks_per_slot = 1; + let p = Pubkey::new_rand(); + let hash = hash(p.as_ref()); + + for _ in 0..1000 { + let bank0 = Arc::new(Bank::new(&genesis_block)); + let bank0_ = bank0.clone(); + let freeze_thread = Builder::new() + .name("freeze".to_string()) + .spawn(move || loop { + if bank0_.tick_height() == bank0_.max_tick_height() { + assert_eq!(bank0_.last_blockhash(), hash); + break; + } + }) + .unwrap(); + + let bank0_ = bank0.clone(); + let register_tick_thread = Builder::new() + .name("register_tick".to_string()) + .spawn(move || { + bank0_.register_tick(&hash); + }) + .unwrap(); + + register_tick_thread.join().unwrap(); + freeze_thread.join().unwrap(); + } +}