use itertools::Itertools;
use kaspa_consensus_core::api::{ConsensusApi, DynConsensus};
use kaspa_core::{core::Core, service::Service};
use parking_lot::RwLock;
use std::{collections::VecDeque, ops::Deref, sync::Arc, thread::JoinHandle};
use tokio::sync::{RwLock as TokioRwLock, RwLockReadGuard as TokioRwLockReadGuard};
pub trait ConsensusCtl: Sync + Send {
fn start(&self) -> Vec<JoinHandle<()>>;
fn stop(&self);
fn make_active(&self);
fn delete(&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);
}
struct MockFactory;
impl ConsensusFactory for MockFactory {
fn new_active_consensus(&self) -> (ConsensusInstance, DynConsensusCtl) {
unimplemented!()
}
fn new_staging_consensus(&self) -> (ConsensusInstance, DynConsensusCtl) {
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 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(Arc::new(TokioRwLock::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();
}
}
}
impl Service for ConsensusManager {
fn ident(self: Arc<Self>) -> &'static str {
"consensus manager"
}
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();
}
}
pub fn cancel(self) {
self.staging.ctl.stop();
for handle in self.handles {
handle.join().unwrap();
}
self.staging.ctl.delete();
}
}
impl Deref for StagingConsensus {
type Target = ConsensusInstance;
fn deref(&self) -> &Self::Target {
&self.staging.consensus
}
}
#[derive(Clone)]
pub struct ConsensusInstance {
session_lock: Arc<TokioRwLock<()>>,
consensus: DynConsensus,
}
impl ConsensusInstance {
pub fn new(session_lock: Arc<TokioRwLock<()>>, consensus: DynConsensus) -> Self {
Self { session_lock, consensus }
}
pub async fn session(&self) -> ConsensusSession<'_> {
let g = self.session_lock.read().await;
ConsensusSession::new(g, self.consensus.clone())
}
}
pub struct ConsensusSession<'a> {
_session_guard: TokioRwLockReadGuard<'a, ()>,
consensus: DynConsensus,
}
impl<'a> ConsensusSession<'a> {
pub fn new(session_guard: TokioRwLockReadGuard<'a, ()>, consensus: DynConsensus) -> Self {
Self { _session_guard: session_guard, consensus }
}
}
impl Deref for ConsensusSession<'_> {
type Target = dyn ConsensusApi;
fn deref(&self) -> &Self::Target {
self.consensus.as_ref()
}
}