use std::collections::HashMap;
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use crate::server::participant::{ConnectionFateWorkItem, ParticipantSemanticError};
use liminal_protocol::wire::ConversationId;
#[derive(Debug, Default)]
pub(in crate::server::participant::production) struct PendingRestores {
gates: Mutex<GateMap>,
repair_incarnation: Mutex<Option<u64>>,
refused: Mutex<RefusedMap>,
deferred_fates: Mutex<HashMap<ConversationId, Vec<ConnectionFateWorkItem>>>,
}
type GateMap = HashMap<ConversationId, Arc<Gate>>;
#[derive(Debug, Default)]
pub(super) struct Gate {
pub(super) state: Mutex<GateState>,
pub(super) changed: Condvar,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(super) enum GateState {
#[default]
Pending,
Running,
Done,
}
impl Gate {
pub(super) fn state(&self) -> Result<MutexGuard<'_, GateState>, ParticipantSemanticError> {
self.state
.lock()
.map_err(|_| ParticipantSemanticError::Internal {
message: "participant restore gate is poisoned".to_owned(),
})
}
pub(super) fn done(&self) -> Result<(), ParticipantSemanticError> {
*self.state()? = GateState::Done;
self.changed.notify_all();
Ok(())
}
}
type RefusedMap = HashMap<ConversationId, Arc<Mutex<Option<u64>>>>;
impl PendingRestores {
pub(super) fn gates(&self) -> Result<MutexGuard<'_, GateMap>, ParticipantSemanticError> {
self.gates
.lock()
.map_err(|_| ParticipantSemanticError::Internal {
message: "participant pending-restore lock is poisoned".to_owned(),
})
}
pub(in crate::server::participant::production) fn register(
&self,
conversation_ids: impl IntoIterator<Item = ConversationId>,
) -> Result<usize, ParticipantSemanticError> {
let mut gates = self.gates()?;
for conversation_id in conversation_ids {
gates.entry(conversation_id).or_default();
}
Ok(gates.len())
}
pub(super) fn gate(
&self,
conversation_id: ConversationId,
) -> Result<Option<Arc<Gate>>, ParticipantSemanticError> {
Ok(self.gates()?.get(&conversation_id).map(Arc::clone))
}
pub(in crate::server::participant::production) fn wake_waiters(
&self,
) -> Result<(), ParticipantSemanticError> {
let gates: Vec<Arc<Gate>> = self.gates()?.values().map(Arc::clone).collect();
for gate in gates {
let state = gate.state()?;
gate.changed.notify_all();
drop(state);
}
Ok(())
}
pub(super) fn deferred(
&self,
) -> Result<
MutexGuard<'_, HashMap<ConversationId, Vec<ConnectionFateWorkItem>>>,
ParticipantSemanticError,
> {
self.deferred_fates
.lock()
.map_err(|_| ParticipantSemanticError::Internal {
message: "participant deferred-fate lock is poisoned".to_owned(),
})
}
pub(in crate::server::participant::production) fn defer_fate_if_pending(
&self,
conversation_id: ConversationId,
work_item: &ConnectionFateWorkItem,
) -> Result<bool, ParticipantSemanticError> {
let Some(gate) = self.gate(conversation_id)? else {
return Ok(false);
};
let state = gate.state()?;
if *state != GateState::Pending {
return Ok(false);
}
self.deferred()?
.entry(conversation_id)
.or_default()
.push(work_item.clone());
drop(state);
Ok(true)
}
pub(super) fn take_deferred(
&self,
conversation_id: ConversationId,
) -> Result<Vec<ConnectionFateWorkItem>, ParticipantSemanticError> {
Ok(self
.deferred()?
.remove(&conversation_id)
.unwrap_or_default())
}
pub(super) fn finish(
&self,
conversation_id: ConversationId,
) -> Result<(), ParticipantSemanticError> {
self.gates()?.remove(&conversation_id);
Ok(())
}
pub(in crate::server::participant::production) fn remaining(
&self,
) -> Result<Vec<ConversationId>, ParticipantSemanticError> {
let mut remaining: Vec<ConversationId> = self.gates()?.keys().copied().collect();
remaining.sort_unstable();
Ok(remaining)
}
pub(in crate::server::participant::production) fn owe_repair(
&self,
incarnation: u64,
) -> Result<(), ParticipantSemanticError> {
*self.repair_incarnation()? = Some(incarnation);
for owed in self.refused()?.values() {
let mut owed = owed.lock().map_err(|_| poisoned_refusal())?;
if owed.is_none() {
*owed = Some(incarnation);
}
}
Ok(())
}
pub(super) fn refused(&self) -> Result<MutexGuard<'_, RefusedMap>, ParticipantSemanticError> {
self.refused.lock().map_err(|_| poisoned_refusal())
}
pub(super) fn refuse(
&self,
conversation_id: ConversationId,
) -> Result<(), ParticipantSemanticError> {
let owed = self.owed_repair()?;
self.refused()?
.entry(conversation_id)
.or_insert_with(|| Arc::new(Mutex::new(owed)));
Ok(())
}
pub(super) fn refused_gate(
&self,
conversation_id: ConversationId,
) -> Result<Option<Arc<Mutex<Option<u64>>>>, ParticipantSemanticError> {
Ok(self.refused()?.get(&conversation_id).map(Arc::clone))
}
pub(super) fn clear_refused(
&self,
conversation_id: ConversationId,
) -> Result<(), ParticipantSemanticError> {
self.refused()?.remove(&conversation_id);
Ok(())
}
pub(super) fn repair_incarnation(
&self,
) -> Result<MutexGuard<'_, Option<u64>>, ParticipantSemanticError> {
self.repair_incarnation
.lock()
.map_err(|_| ParticipantSemanticError::Internal {
message: "participant repair-incarnation lock is poisoned".to_owned(),
})
}
pub(in crate::server::participant::production) fn owed_repair(
&self,
) -> Result<Option<u64>, ParticipantSemanticError> {
Ok(*self.repair_incarnation()?)
}
}
pub(super) fn poisoned_refusal() -> ParticipantSemanticError {
ParticipantSemanticError::Internal {
message: "participant refused-restore lock is poisoned".to_owned(),
}
}