use itertools::Itertools;
use kaspa_consensus_core::api::{ConsensusApi, DynConsensus};
use kaspa_core::{core::Core, debug, service::Service};
use parking_lot::RwLock;
use std::{collections::VecDeque, ops::Deref, sync::Arc, thread::JoinHandle};
mod batch;
mod session;
pub use batch::BlockProcessingBatch;
pub use session::{
spawn_blocking, ConsensusInstance, ConsensusProxy, ConsensusSessionBlocking, ConsensusSessionOwned, SessionLock, SessionReadGuard,
SessionWriteGuard,
};
pub trait ConsensusCtl: Sync + Send {
fn start(&self) -> Vec<JoinHandle<()>>;
fn stop(&self);
fn make_active(&self);
}
pub type DynConsensusCtl = Arc<dyn ConsensusCtl>;
pub trait ConsensusFactory: Sync + Send {
fn new_active_consensus(&self) -> (ConsensusInstance, DynConsensusCtl);
fn new_staging_consensus(&self) -> (ConsensusInstance, DynConsensusCtl);
fn close(&self);
fn delete_inactive_consensus_entries(&self);
fn delete_staging_entry(&self);
}
struct MockFactory;
impl ConsensusFactory for MockFactory {
fn new_active_consensus(&self) -> (ConsensusInstance, DynConsensusCtl) {
unimplemented!()
}
fn new_staging_consensus(&self) -> (ConsensusInstance, DynConsensusCtl) {
unimplemented!()
}
fn close(&self) {
unimplemented!()
}
fn delete_inactive_consensus_entries(&self) {
unimplemented!()
}
fn delete_staging_entry(&self) {
unimplemented!()
}
}
pub trait ConsensusResetHandler: Send + Sync {
fn handle_consensus_reset(&self);
}
struct ConsensusInner {
consensus: ConsensusInstance,
ctl: DynConsensusCtl,
}
impl ConsensusInner {
fn new(consensus: ConsensusInstance, ctl: DynConsensusCtl) -> Self {
Self { consensus, ctl }
}
}
struct ManagerInner {
current: ConsensusInner,
handles: VecDeque<JoinHandle<()>>,
consensus_reset_handlers: Vec<Arc<dyn ConsensusResetHandler>>,
}
impl ManagerInner {
fn new(consensus: ConsensusInstance, ctl: DynConsensusCtl) -> Self {
Self {
current: ConsensusInner::new(consensus, ctl),
handles: Default::default(),
consensus_reset_handlers: Default::default(),
}
}
}
pub struct ConsensusManager {
factory: Arc<dyn ConsensusFactory>,
inner: RwLock<ManagerInner>,
}
impl ConsensusManager {
pub const IDENT: &'static str = "consensus manager";
pub fn new(factory: Arc<dyn ConsensusFactory>) -> Self {
let (consensus, ctl) = factory.new_active_consensus();
Self { factory, inner: RwLock::new(ManagerInner::new(consensus, ctl)) }
}
pub fn from_consensus<T: ConsensusApi + ConsensusCtl + 'static>(consensus: Arc<T>) -> Self {
let (consensus, ctl) = (consensus.clone() as DynConsensus, consensus as DynConsensusCtl);
Self {
factory: Arc::new(MockFactory),
inner: RwLock::new(ManagerInner::new(ConsensusInstance::new(SessionLock::new(), consensus), ctl)),
}
}
pub fn consensus(&self) -> ConsensusInstance {
self.inner.read().current.consensus.clone()
}
pub fn new_staging_consensus(self: &Arc<Self>) -> StagingConsensus {
let (consensus, ctl) = self.factory.new_staging_consensus();
StagingConsensus::new(self.clone(), ConsensusInner::new(consensus, ctl))
}
pub fn register_consensus_reset_handler(&self, handler: Arc<dyn ConsensusResetHandler>) {
self.inner.write().consensus_reset_handlers.push(handler);
}
fn worker(&self) {
let handles = self.inner.read().current.ctl.clone().start();
self.inner.write().handles.extend(handles);
let mut g = self.inner.write();
while let Some(handle) = g.handles.pop_front() {
drop(g);
handle.join().unwrap();
g = self.inner.write();
}
debug!("[Consensus manager] all consensus threads exited");
self.factory.close();
}
pub fn delete_inactive_consensus_entries(&self) {
self.factory.delete_inactive_consensus_entries();
}
pub fn delete_staging_entry(&self) {
self.factory.delete_staging_entry();
}
}
impl Service for ConsensusManager {
fn ident(self: Arc<Self>) -> &'static str {
Self::IDENT
}
fn start(self: Arc<Self>, _core: Arc<Core>) -> Vec<JoinHandle<()>> {
vec![std::thread::spawn(move || self.worker())]
}
fn stop(self: Arc<Self>) {
self.inner.read().current.ctl.clone().stop();
}
}
pub struct StagingConsensus {
manager: Arc<ConsensusManager>,
staging: ConsensusInner,
handles: VecDeque<JoinHandle<()>>,
}
impl StagingConsensus {
fn new(manager: Arc<ConsensusManager>, staging: ConsensusInner) -> Self {
let handles = VecDeque::from_iter(staging.ctl.start());
Self { manager, staging, handles }
}
pub fn commit(self) {
let mut g = self.manager.inner.write();
let prev = std::mem::replace(&mut g.current, self.staging);
g.handles.extend(self.handles);
prev.ctl.stop();
g.current.ctl.make_active();
drop(g);
let handlers = self.manager.inner.read().consensus_reset_handlers.iter().cloned().collect_vec();
for handler in handlers {
handler.handle_consensus_reset();
}
drop(prev);
self.manager.delete_inactive_consensus_entries();
}
pub fn cancel(self) {
self.staging.ctl.stop();
for handle in self.handles {
handle.join().unwrap();
}
drop(self.staging);
self.manager.delete_staging_entry();
}
}
impl Deref for StagingConsensus {
type Target = ConsensusInstance;
fn deref(&self) -> &Self::Target {
&self.staging.consensus
}
}