use {
arc_swap::ArcSwapOption,
solana_clock::Slot,
std::{
sync::Arc,
time::{Duration, Instant},
},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AlpenglowSlotInfo {
pub slot: Slot,
pub started_at: Instant,
pub slot_duration: Duration,
}
#[derive(Clone, Default)]
pub struct SharedAlpenglowSlotClock(Arc<ArcSwapOption<AlpenglowSlotInfo>>);
impl SharedAlpenglowSlotClock {
pub fn update(&self, slot: Slot, started_at: Instant, slot_duration: Duration) {
let slot_info = Arc::new(AlpenglowSlotInfo {
slot,
started_at,
slot_duration,
});
self.0.rcu(|current| {
if current.as_ref().is_some_and(|current| current.slot >= slot) {
current.clone()
} else {
Some(slot_info.clone())
}
});
}
pub fn update_leader(&self, slot: Slot, started_at: Instant, slot_duration: Duration) {
let slot_info = Arc::new(AlpenglowSlotInfo {
slot,
started_at,
slot_duration,
});
self.0.rcu(|current| {
if current.as_ref().is_some_and(|current| current.slot > slot) {
current.clone()
} else {
Some(slot_info.clone())
}
});
}
pub fn load(&self) -> Option<AlpenglowSlotInfo> {
self.0.load().as_ref().map(|slot_info| **slot_info)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shared_alpenglow_slot_clock() {
let clock = SharedAlpenglowSlotClock::default();
let started_at = Instant::now();
let slot_duration = Duration::from_millis(400);
assert_eq!(clock.load(), None);
clock.update(4, started_at, slot_duration);
let slot_info = AlpenglowSlotInfo {
slot: 4,
started_at,
slot_duration,
};
assert_eq!(clock.load(), Some(slot_info));
clock.update(
4,
started_at + Duration::from_millis(1),
Duration::from_millis(200),
);
clock.update(3, started_at, slot_duration);
assert_eq!(clock.load(), Some(slot_info));
let next_started_at = started_at + slot_duration;
clock.update(5, next_started_at, Duration::from_millis(200));
assert_eq!(
clock.load(),
Some(AlpenglowSlotInfo {
slot: 5,
started_at: next_started_at,
slot_duration: Duration::from_millis(200),
})
);
let replacement_started_at = next_started_at + Duration::from_millis(2);
clock.update_leader(5, replacement_started_at, Duration::from_millis(200));
assert_eq!(
clock.load(),
Some(AlpenglowSlotInfo {
slot: 5,
started_at: replacement_started_at,
slot_duration: Duration::from_millis(200),
})
);
clock.update_leader(4, started_at, slot_duration);
assert_eq!(clock.load().unwrap().slot, 5);
}
}