solana_accounts_db/epoch_accounts_hash/
manager.rs1use {
2 super::EpochAccountsHash,
3 solana_clock::Slot,
4 std::sync::{Condvar, Mutex},
5};
6
7#[derive(Debug)]
12pub struct Manager {
13 state: Mutex<State>,
15 cvar: Condvar,
17}
18
19impl Manager {
20 #[must_use]
21 fn _new(state: State) -> Self {
22 Self {
23 state: Mutex::new(state),
24 cvar: Condvar::new(),
25 }
26 }
27
28 #[must_use]
30 pub fn new_invalid() -> Self {
31 Self::_new(State::Invalid)
32 }
33
34 #[must_use]
36 pub fn new_valid(epoch_accounts_hash: EpochAccountsHash, slot: Slot) -> Self {
37 Self::_new(State::Valid(epoch_accounts_hash, slot))
38 }
39
40 pub fn set_in_flight(&self, slot: Slot) {
42 let mut state = self.state.lock().unwrap();
43 if let State::InFlight(old_slot) = &*state {
44 panic!("An epoch accounts hash calculation is already in-flight from slot {old_slot}!");
45 }
46 *state = State::InFlight(slot);
47 }
48
49 pub fn set_valid(&self, epoch_accounts_hash: EpochAccountsHash, slot: Slot) {
51 let mut state = self.state.lock().unwrap();
52 if let State::Valid(old_epoch_accounts_hash, old_slot) = &*state {
53 panic!(
54 "The epoch accounts hash is already valid! \
55 \nold slot: {old_slot}, epoch accounts hash: {old_epoch_accounts_hash:?} \
56 \nnew slot: {slot}, epoch accounts hash: {epoch_accounts_hash:?}"
57 );
58 }
59 *state = State::Valid(epoch_accounts_hash, slot);
60 self.cvar.notify_all();
61 }
62
63 pub fn wait_get_epoch_accounts_hash(&self) -> EpochAccountsHash {
67 let mut state = self.state.lock().unwrap();
68 loop {
69 match &*state {
70 State::Valid(epoch_accounts_hash, _slot) => break *epoch_accounts_hash,
71 State::InFlight(_slot) => state = self.cvar.wait(state).unwrap(),
72 State::Invalid => panic!("The epoch accounts hash cannot be awaited when Invalid!"),
73 }
74 }
75 }
76
77 pub fn try_get_epoch_accounts_hash(&self) -> Option<EpochAccountsHash> {
81 let state = self.state.lock().unwrap();
82 match &*state {
83 State::Valid(epoch_accounts_hash, _slot) => Some(*epoch_accounts_hash),
84 _ => None,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93enum State {
94 Invalid,
97 InFlight(Slot),
100 Valid(EpochAccountsHash, Slot),
102}
103
104#[cfg(test)]
105mod tests {
106 use {super::*, solana_hash::Hash, std::time::Duration};
107
108 #[test]
109 fn test_new_valid() {
110 let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
111 let manager = Manager::new_valid(epoch_accounts_hash, 5678);
112 assert_eq!(
113 manager.try_get_epoch_accounts_hash(),
114 Some(epoch_accounts_hash),
115 );
116 assert_eq!(manager.wait_get_epoch_accounts_hash(), epoch_accounts_hash);
117 }
118
119 #[test]
120 fn test_new_invalid() {
121 let manager = Manager::new_invalid();
122 assert!(manager.try_get_epoch_accounts_hash().is_none());
123 }
124
125 #[test]
126 fn test_try_get_epoch_accounts_hash() {
127 let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
128 for (state, expected) in [
129 (State::Invalid, None),
130 (State::InFlight(123), None),
131 (
132 State::Valid(epoch_accounts_hash, 5678),
133 Some(epoch_accounts_hash),
134 ),
135 ] {
136 let manager = Manager::_new(state);
137 let actual = manager.try_get_epoch_accounts_hash();
138 assert_eq!(actual, expected);
139 }
140 }
141
142 #[test]
143 fn test_wait_epoch_accounts_hash() {
144 {
146 let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
147 let manager = Manager::new_valid(epoch_accounts_hash, 5678);
148 assert_eq!(manager.wait_get_epoch_accounts_hash(), epoch_accounts_hash);
149 }
150
151 {
153 let epoch_accounts_hash = EpochAccountsHash::new(Hash::new_unique());
154 let manager = Manager::new_invalid();
155 manager.set_in_flight(123);
156
157 std::thread::scope(|s| {
158 s.spawn(|| {
159 std::thread::sleep(Duration::from_secs(1));
160 manager.set_valid(epoch_accounts_hash, 5678)
161 });
162 assert!(manager.try_get_epoch_accounts_hash().is_none());
163 assert_eq!(manager.wait_get_epoch_accounts_hash(), epoch_accounts_hash);
164 assert!(manager.try_get_epoch_accounts_hash().is_some());
165 });
166 }
167 }
168
169 #[test]
170 #[should_panic]
171 fn test_wait_epoch_accounts_hash_invalid() {
172 let manager = Manager::new_invalid();
174 let _epoch_accounts_hash = manager.wait_get_epoch_accounts_hash();
175 }
176}