solana_accounts_db/epoch_accounts_hash/
manager.rsuse {
super::EpochAccountsHash,
solana_sdk::clock::Slot,
std::sync::{Condvar, Mutex},
};
#[derive(Debug)]
pub struct Manager {
state: Mutex<State>,
cvar: Condvar,
}
impl Manager {
#[must_use]
fn _new(state: State) -> Self {
Self {
state: Mutex::new(state),
cvar: Condvar::new(),
}
}
#[must_use]
pub fn new_invalid() -> Self {
Self::_new(State::Invalid)
}
#[must_use]
pub fn new_valid(epoch_accounts_hash: EpochAccountsHash, slot: Slot) -> Self {
Self::_new(State::Valid(epoch_accounts_hash, slot))
}
pub fn set_in_flight(&self, slot: Slot) {
let mut state = self.state.lock().unwrap();
if let State::InFlight(old_slot) = &*state {
panic!("An epoch accounts hash calculation is already in-flight from slot {old_slot}!");
}
*state = State::InFlight(slot);
}
pub fn set_valid(&self, epoch_accounts_hash: EpochAccountsHash, slot: Slot) {
let mut state = self.state.lock().unwrap();
if let State::Valid(old_epoch_accounts_hash, old_slot) = &*state {
panic!(
"The epoch accounts hash is already valid! \
\nold slot: {old_slot}, epoch accounts hash: {old_epoch_accounts_hash:?} \
\nnew slot: {slot}, epoch accounts hash: {epoch_accounts_hash:?}"
);
}
*state = State::Valid(epoch_accounts_hash, slot);
self.cvar.notify_all();
}
pub fn wait_get_epoch_accounts_hash(&self) -> EpochAccountsHash {
let mut state = self.state.lock().unwrap();
loop {
match &*state {
State::Valid(epoch_accounts_hash, _slot) => break *epoch_accounts_hash,
State::InFlight(_slot) => state = self.cvar.wait(state).unwrap(),
State::Invalid => panic!("The epoch accounts hash cannot be awaited when Invalid!"),
}
}
}
pub fn try_get_epoch_accounts_hash(&self) -> Option<EpochAccountsHash> {
let state = self.state.lock().unwrap();
match &*state {
State::Valid(epoch_accounts_hash, _slot) => Some(*epoch_accounts_hash),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
Invalid,
InFlight(Slot),
Valid(EpochAccountsHash, Slot),
}
#[cfg(test)]
mod tests {
use {super::*, solana_sdk::hash::Hash, std::time::Duration};
#[test]
fn test_new_valid() {
let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
let manager = Manager::new_valid(epoch_accounts_hash, 5678);
assert_eq!(
manager.try_get_epoch_accounts_hash(),
Some(epoch_accounts_hash),
);
assert_eq!(manager.wait_get_epoch_accounts_hash(), epoch_accounts_hash);
}
#[test]
fn test_new_invalid() {
let manager = Manager::new_invalid();
assert!(manager.try_get_epoch_accounts_hash().is_none());
}
#[test]
fn test_try_get_epoch_accounts_hash() {
let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
for (state, expected) in [
(State::Invalid, None),
(State::InFlight(123), None),
(
State::Valid(epoch_accounts_hash, 5678),
Some(epoch_accounts_hash),
),
] {
let manager = Manager::_new(state);
let actual = manager.try_get_epoch_accounts_hash();
assert_eq!(actual, expected);
}
}
#[test]
fn test_wait_epoch_accounts_hash() {
{
let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
let manager = Manager::new_valid(epoch_accounts_hash, 5678);
assert_eq!(manager.wait_get_epoch_accounts_hash(), epoch_accounts_hash);
}
{
let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
let manager = Manager::new_invalid();
manager.set_in_flight(123);
std::thread::scope(|s| {
s.spawn(|| {
std::thread::sleep(Duration::from_secs(1));
manager.set_valid(epoch_accounts_hash, 5678)
});
assert!(manager.try_get_epoch_accounts_hash().is_none());
assert_eq!(manager.wait_get_epoch_accounts_hash(), epoch_accounts_hash);
assert!(manager.try_get_epoch_accounts_hash().is_some());
});
}
}
#[test]
#[should_panic]
fn test_wait_epoch_accounts_hash_invalid() {
let manager = Manager::new_invalid();
let _epoch_accounts_hash = manager.wait_get_epoch_accounts_hash();
}
}