use std::iter::FusedIterator;
use thin_vec::{ThinVec, thin_vec};
use crate::key::DatabaseKeyIndex;
use crate::sync::OnceLock;
use crate::sync::atomic::{AtomicBool, AtomicU16, Ordering};
use crate::{Id, Revision};
pub const MAX_ITERATIONS: u8 = 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,
#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) iteration: AtomicIterationStamp,
#[cfg_attr(feature = "persistence", serde(skip))]
removed: AtomicBool,
}
impl CycleHead {
pub const fn new(database_key_index: DatabaseKeyIndex, iteration: IterationStamp) -> Self {
Self {
database_key_index,
iteration: AtomicIterationStamp(AtomicU16::new(iteration.0)),
removed: AtomicBool::new(false),
}
}
}
impl Clone for CycleHead {
fn clone(&self) -> Self {
Self {
database_key_index: self.database_key_index,
iteration: self.iteration.load().into(),
removed: self.removed.load(Ordering::Relaxed).into(),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct IterationStamp(u16);
impl IterationStamp {
const fn new(iteration: u8, cancellation_count: u8) -> Self {
Self(u16::from_le_bytes([iteration, cancellation_count]))
}
pub(crate) const fn initial(cancellation_count: u8) -> Self {
Self::new(0, cancellation_count)
}
pub(crate) const fn is_default(self) -> bool {
self.0 == 0
}
pub(crate) const fn is_initial_iteration(self) -> bool {
self.iteration() == 0
}
pub(crate) const fn increment_iteration(self) -> Option<Self> {
let next = Self(self.0 + 1);
if next.iteration() <= MAX_ITERATIONS {
Some(next)
} else {
None
}
}
pub(crate) const fn iteration_as_u32(self) -> u32 {
self.iteration() as u32
}
pub(crate) const fn cancellation_count(self) -> u8 {
self.0.to_le_bytes()[1]
}
pub(crate) const fn iteration(self) -> u8 {
self.0.to_le_bytes()[0]
}
}
impl std::fmt::Debug for IterationStamp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IterationStamp")
.field("iteration", &self.iteration())
.field("cancellation", &self.cancellation_count())
.finish()
}
}
#[derive(Debug, Default)]
pub(crate) struct AtomicIterationStamp(AtomicU16);
impl AtomicIterationStamp {
pub(crate) fn load(&self) -> IterationStamp {
IterationStamp(self.0.load(Ordering::Relaxed))
}
pub(crate) fn load_mut(&mut self) -> IterationStamp {
IterationStamp(*self.0.get_mut())
}
pub(crate) fn store_iteration(&self, iteration: IterationStamp) {
debug_assert_eq!(
self.load().cancellation_count(),
iteration.cancellation_count()
);
self.0.store(iteration.0, Ordering::Release);
}
pub(crate) fn set_iteration(&mut self, iteration: IterationStamp) {
debug_assert_eq!(
self.load_mut().cancellation_count(),
iteration.cancellation_count()
);
*self.0.get_mut() = iteration.0;
}
}
impl From<IterationStamp> for AtomicIterationStamp {
fn from(iteration: IterationStamp) -> Self {
AtomicIterationStamp(iteration.0.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: IterationStamp) -> Self {
Self(thin_vec![CycleHead {
database_key_index,
iteration: iteration.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: IterationStamp,
) {
if let Some(cycle_head) = self
.0
.iter_mut()
.find(|cycle_head| cycle_head.database_key_index == cycle_head_index)
{
cycle_head.iteration.set_iteration(new_iteration);
}
}
pub(crate) fn update_iteration_count(
&self,
cycle_head_index: DatabaseKeyIndex,
new_iteration: IterationStamp,
) {
if let Some(cycle_head) = self
.0
.iter()
.find(|cycle_head| cycle_head.database_key_index == cycle_head_index)
{
cycle_head.iteration.store_iteration(new_iteration);
}
}
#[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.load());
}
}
pub(crate) fn insert(
&mut self,
database_key_index: DatabaseKeyIndex,
iteration: IterationStamp,
) -> 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 = iteration.into();
true
} else {
let existing_iteration = existing.iteration.load_mut();
assert_eq!(
existing_iteration, iteration,
"Can't merge cycle heads {:?} with different iterations ({existing_iteration:?}, {iteration:?})",
existing.database_key_index
);
false
}
} else {
self.0.push(CycleHead::new(database_key_index, iteration));
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: IterationStamp,
verified_at: Revision,
cycle_heads: &'db CycleHeads,
},
Final {
iteration: IterationStamp,
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 { .. })
}
}