use std::iter::FusedIterator;
use thin_vec::{ThinVec, thin_vec};
use crate::key::DatabaseKeyIndex;
use crate::sync::OnceLock;
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use crate::{Id, Revision};
pub const MAX_ITERATIONS: IterationCount = IterationCount(200);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CycleRecoveryStrategy {
Panic,
Fixpoint,
FallbackImmediate,
}
#[derive(Debug)]
#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
pub struct CycleHead {
pub(crate) database_key_index: DatabaseKeyIndex,
pub(crate) iteration_count: AtomicIterationCount,
#[cfg_attr(feature = "persistence", serde(skip))]
removed: AtomicBool,
}
impl CycleHead {
pub const fn new(
database_key_index: DatabaseKeyIndex,
iteration_count: IterationCount,
) -> Self {
Self {
database_key_index,
iteration_count: AtomicIterationCount(AtomicU8::new(iteration_count.0)),
removed: AtomicBool::new(false),
}
}
}
impl Clone for CycleHead {
fn clone(&self) -> Self {
Self {
database_key_index: self.database_key_index,
iteration_count: self.iteration_count.load().into(),
removed: self.removed.load(Ordering::Relaxed).into(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "persistence", serde(transparent))]
pub struct IterationCount(u8);
impl IterationCount {
pub(crate) const fn initial() -> Self {
Self(0)
}
pub(crate) const fn is_initial(self) -> bool {
self.0 == 0
}
pub(crate) const fn increment(self) -> Option<Self> {
let next = Self(self.0 + 1);
if next.0 <= MAX_ITERATIONS.0 {
Some(next)
} else {
None
}
}
pub(crate) const fn as_u32(self) -> u32 {
self.0 as u32
}
}
impl std::fmt::Display for IterationCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug)]
pub(crate) struct AtomicIterationCount(AtomicU8);
impl AtomicIterationCount {
pub(crate) fn load(&self) -> IterationCount {
IterationCount(self.0.load(Ordering::Relaxed))
}
pub(crate) fn load_mut(&mut self) -> IterationCount {
IterationCount(*self.0.get_mut())
}
pub(crate) fn store(&self, value: IterationCount) {
self.0.store(value.0, Ordering::Release);
}
pub(crate) fn set(&mut self, value: IterationCount) {
*self.0.get_mut() = value.0;
}
}
impl std::fmt::Display for AtomicIterationCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.load().fmt(f)
}
}
impl From<IterationCount> for AtomicIterationCount {
fn from(iteration_count: IterationCount) -> Self {
AtomicIterationCount(iteration_count.0.into())
}
}
#[cfg(feature = "persistence")]
impl serde::Serialize for AtomicIterationCount {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.load().serialize(serializer)
}
}
#[cfg(feature = "persistence")]
impl<'de> serde::Deserialize<'de> for AtomicIterationCount {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
IterationCount::deserialize(deserializer).map(Into::into)
}
}
#[derive(Clone, Debug, Default)]
pub struct CycleHeads(ThinVec<CycleHead>);
impl CycleHeads {
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn initial(
database_key_index: DatabaseKeyIndex,
iteration_count: IterationCount,
) -> Self {
Self(thin_vec![CycleHead {
database_key_index,
iteration_count: iteration_count.into(),
removed: false.into()
}])
}
pub(crate) fn iter(&self) -> CycleHeadsIterator<'_> {
CycleHeadsIterator {
inner: self.0.iter(),
}
}
pub(crate) fn ids(&self) -> CycleHeadIdsIterator<'_> {
CycleHeadIdsIterator { inner: self.iter() }
}
pub(crate) fn iter_not_eq(
&self,
own: DatabaseKeyIndex,
) -> impl DoubleEndedIterator<Item = &CycleHead> {
self.iter()
.filter(move |head| head.database_key_index != own)
}
pub(crate) fn contains(&self, value: &DatabaseKeyIndex) -> bool {
self.into_iter()
.any(|head| head.database_key_index == *value)
}
pub(crate) fn remove_all_except(&self, except: DatabaseKeyIndex) {
for head in self.0.iter() {
if head.database_key_index == except {
continue;
}
head.removed.store(true, Ordering::Release);
}
}
pub(crate) fn update_iteration_count_mut(
&mut self,
cycle_head_index: DatabaseKeyIndex,
new_iteration_count: IterationCount,
) {
if let Some(cycle_head) = self
.0
.iter_mut()
.find(|cycle_head| cycle_head.database_key_index == cycle_head_index)
{
cycle_head.iteration_count.set(new_iteration_count);
}
}
pub(crate) fn update_iteration_count(
&self,
cycle_head_index: DatabaseKeyIndex,
new_iteration_count: IterationCount,
) {
if let Some(cycle_head) = self
.0
.iter()
.find(|cycle_head| cycle_head.database_key_index == cycle_head_index)
{
cycle_head.iteration_count.store(new_iteration_count);
}
}
#[inline]
pub(crate) fn extend(&mut self, other: &Self) {
self.0.reserve(other.0.len());
for head in other {
debug_assert!(!head.removed.load(Ordering::Relaxed));
self.insert(head.database_key_index, head.iteration_count.load());
}
}
pub(crate) fn insert(
&mut self,
database_key_index: DatabaseKeyIndex,
iteration_count: IterationCount,
) -> bool {
if let Some(existing) = self
.0
.iter_mut()
.find(|candidate| candidate.database_key_index == database_key_index)
{
let removed = existing.removed.get_mut();
if *removed {
*removed = false;
existing.iteration_count.set(iteration_count);
true
} else {
let existing_count = existing.iteration_count.load_mut();
assert_eq!(
existing_count, iteration_count,
"Can't merge cycle heads {:?} with different iteration counts ({existing_count:?}, {iteration_count:?})",
existing.database_key_index
);
false
}
} else {
self.0
.push(CycleHead::new(database_key_index, iteration_count));
true
}
}
#[cfg(feature = "salsa_unstable")]
pub(crate) fn allocation_size(&self) -> usize {
std::mem::size_of_val(self.0.as_slice())
}
}
#[cfg(feature = "persistence")]
impl serde::Serialize for CycleHeads {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(None)?;
for e in self {
if e.removed.load(Ordering::Relaxed) {
continue;
}
seq.serialize_element(e)?;
}
seq.end()
}
}
#[cfg(feature = "persistence")]
impl<'de> serde::Deserialize<'de> for CycleHeads {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let vec: ThinVec<CycleHead> = serde::Deserialize::deserialize(deserializer)?;
Ok(CycleHeads(vec))
}
}
impl IntoIterator for CycleHeads {
type Item = CycleHead;
type IntoIter = <ThinVec<Self::Item> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone)]
pub struct CycleHeadsIterator<'a> {
inner: std::slice::Iter<'a, CycleHead>,
}
impl<'a> Iterator for CycleHeadsIterator<'a> {
type Item = &'a CycleHead;
fn next(&mut self) -> Option<Self::Item> {
loop {
let next = self.inner.next()?;
if next.removed.load(Ordering::Relaxed) {
continue;
}
return Some(next);
}
}
}
impl FusedIterator for CycleHeadsIterator<'_> {}
impl DoubleEndedIterator for CycleHeadsIterator<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
let next = self.inner.next_back()?;
if next.removed.load(Ordering::Relaxed) {
continue;
}
return Some(next);
}
}
}
impl<'a> std::iter::IntoIterator for &'a CycleHeads {
type Item = &'a CycleHead;
type IntoIter = CycleHeadsIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl From<CycleHead> for CycleHeads {
fn from(value: CycleHead) -> Self {
Self(thin_vec![value])
}
}
#[inline]
pub(crate) fn empty_cycle_heads() -> &'static CycleHeads {
static EMPTY_CYCLE_HEADS: OnceLock<CycleHeads> = OnceLock::new();
EMPTY_CYCLE_HEADS.get_or_init(|| CycleHeads(ThinVec::new()))
}
#[derive(Clone)]
pub struct CycleHeadIdsIterator<'a> {
inner: CycleHeadsIterator<'a>,
}
impl Iterator for CycleHeadIdsIterator<'_> {
type Item = crate::Id;
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|head| head.database_key_index.key_index())
}
}
pub struct Cycle<'a> {
pub(crate) head_ids: CycleHeadIdsIterator<'a>,
pub(crate) id: Id,
pub(crate) iteration: u32,
}
impl Cycle<'_> {
pub fn head_ids(&self) -> CycleHeadIdsIterator<'_> {
self.head_ids.clone()
}
pub fn id(&self) -> Id {
self.id
}
pub fn iteration(&self) -> u32 {
self.iteration
}
}
#[derive(Debug)]
pub enum ProvisionalStatus<'db> {
Provisional {
iteration: IterationCount,
verified_at: Revision,
cycle_heads: &'db CycleHeads,
},
Final {
iteration: IterationCount,
verified_at: Revision,
},
}
impl<'db> ProvisionalStatus<'db> {
pub(crate) fn cycle_heads(&self) -> &'db CycleHeads {
match self {
ProvisionalStatus::Provisional { cycle_heads, .. } => cycle_heads,
ProvisionalStatus::Final { .. } => empty_cycle_heads(),
}
}
pub(crate) const fn is_provisional(&self) -> bool {
matches!(self, ProvisionalStatus::Provisional { .. })
}
}