use {
crossbeam_channel::{Receiver, SendError, Sender, TryRecvError},
solana_clock::Slot,
solana_runtime::{bank::Bank, installed_scheduler_pool::BankWithScheduler},
std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
};
pub enum PohServiceMessage {
Reset {
reset_bank: Arc<Bank>,
next_leader_slot: Option<(Slot, Slot)>,
},
SetBank {
bank: BankWithScheduler,
},
}
pub struct PohController {
sender: Sender<PohServiceMessage>,
pending_message: Arc<AtomicUsize>,
}
impl PohController {
pub fn new() -> (Self, PohServiceMessageReceiver) {
const CHANNEL_SIZE: usize = 16; let (sender, receiver) = crossbeam_channel::bounded(CHANNEL_SIZE);
let pending_message = Arc::new(AtomicUsize::new(0));
let receiver = PohServiceMessageReceiver {
receiver,
pending_message: pending_message.clone(),
};
(
Self {
sender,
pending_message,
},
receiver,
)
}
pub fn has_pending_message(&self) -> bool {
self.pending_message.load(Ordering::Acquire) > 0
}
pub fn set_bank_sync(
&mut self,
bank: BankWithScheduler,
) -> Result<(), SendError<PohServiceMessage>> {
self.send_and_wait_on_pending_message(PohServiceMessage::SetBank { bank })
}
pub fn set_bank(
&mut self,
bank: BankWithScheduler,
) -> Result<(), SendError<PohServiceMessage>> {
self.send_message(PohServiceMessage::SetBank { bank })
}
pub fn reset_sync(
&mut self,
reset_bank: Arc<Bank>,
next_leader_slot: Option<(Slot, Slot)>,
) -> Result<(), SendError<PohServiceMessage>> {
self.send_and_wait_on_pending_message(PohServiceMessage::Reset {
reset_bank,
next_leader_slot,
})
}
pub fn reset(
&mut self,
reset_bank: Arc<Bank>,
next_leader_slot: Option<(Slot, Slot)>,
) -> Result<(), SendError<PohServiceMessage>> {
self.send_message(PohServiceMessage::Reset {
reset_bank,
next_leader_slot,
})
}
fn send_and_wait_on_pending_message(
&mut self,
message: PohServiceMessage,
) -> Result<(), SendError<PohServiceMessage>> {
self.send_message(message)?;
while self.has_pending_message() {
core::hint::spin_loop();
}
Ok(())
}
fn send_message(
&mut self,
message: PohServiceMessage,
) -> Result<(), SendError<PohServiceMessage>> {
self.pending_message.fetch_add(1, Ordering::AcqRel);
self.sender.send(message)?;
Ok(())
}
}
pub struct PohServiceMessageReceiver {
receiver: Receiver<PohServiceMessage>,
pending_message: Arc<AtomicUsize>,
}
impl PohServiceMessageReceiver {
pub(crate) fn try_recv(&self) -> Result<PohServiceMessageGuard<'_>, TryRecvError> {
self.receiver
.try_recv()
.map(|message| PohServiceMessageGuard {
message_receiver: self,
message: Some(message),
})
}
}
pub(crate) struct PohServiceMessageGuard<'a> {
message_receiver: &'a PohServiceMessageReceiver,
message: Option<PohServiceMessage>,
}
impl PohServiceMessageGuard<'_> {
pub(crate) fn take(&mut self) -> PohServiceMessage {
self.message.take().unwrap()
}
}
impl Drop for PohServiceMessageGuard<'_> {
fn drop(&mut self) {
if self.message.is_none() {
self.message_receiver
.pending_message
.fetch_sub(1, Ordering::AcqRel);
} else {
panic!("PohServiceMessageGuard dropped without processing the message");
}
}
}