Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/salsa-macro-rules/src/setup_tracked_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ macro_rules! setup_tracked_fn {

type Eviction = $Eviction;

const CYCLE_STRATEGY: $zalsa::CycleRecoveryStrategy = $zalsa::CycleRecoveryStrategy::$cycle_recovery_strategy;
type CycleStrategy = $zalsa::function::cycle_strategy::$cycle_recovery_strategy;

$($values_equal)+

Expand Down
60 changes: 8 additions & 52 deletions src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::ptr::NonNull;
use std::sync::OnceLock;
use std::sync::atomic::Ordering;

use self::cycle_strategy::CycleStrategy as _;
use crate::cycle::{CycleRecoveryStrategy, IterationStamp, ProvisionalStatus};
use crate::database::RawDatabase;
use crate::function::delete::DeletedEntries;
Expand All @@ -27,6 +28,8 @@ use crate::{Cycle, Id, Revision};
#[cfg(feature = "accumulator")]
mod accumulated;
mod backdate;
#[doc(hidden)]
pub mod cycle_strategy;
mod delete;
mod diff_outputs;
mod eviction;
Expand All @@ -49,7 +52,7 @@ pub type Memo<C> = memo::Memo<C>;
/// after erasing `'db` and to use after rebranding it with a later database
/// lifetime. This is guaranteed when the output implements [`crate::SalsaValue`]
/// or when it is the same `'static` type for every `'db`.
pub unsafe trait Configuration: Any {
pub unsafe trait Configuration: Any + Sized {
const DEBUG_NAME: &'static str;
const LOCATION: crate::ingredient::Location;
const PERSIST: bool;
Expand All @@ -73,7 +76,9 @@ pub unsafe trait Configuration: Any {

/// Determines whether this function can recover from being a participant in a cycle
/// (and, if so, how).
const CYCLE_STRATEGY: CycleRecoveryStrategy;
type CycleStrategy: cycle_strategy::CycleStrategy<Self>;

const CYCLE_RECOVERY_STRATEGY: CycleRecoveryStrategy = Self::CycleStrategy::RECOVERY_STRATEGY;

/// Invokes after a new result `new_value` has been computed for which an older memoized value
/// existed `old_value`, or in fixpoint iteration. Returns true if the new value is equal to
Expand Down Expand Up @@ -179,38 +184,12 @@ impl<'db> FunctionIngredientRef<'db> {
pub(crate) fn sync_table(&self) -> &'db SyncTable {
self.ingredient.sync_table()
}

/// Returns information about the current provisional status of `input`.
///
/// Is it a provisional value, a poisoned provisional memo, or has it been finalized and in
/// which iteration.
///
/// Returns `None` if `input` doesn't exist.
pub(crate) fn provisional_status(
&self,
zalsa: &'db Zalsa,
input: Id,
) -> Option<ProvisionalStatus<'db>> {
self.ingredient.provisional_status(zalsa, input)
}
}

pub(crate) trait FunctionIngredient: Send + Sync {
fn memo<'db>(&'db self, zalsa: &'db Zalsa, input: Id) -> Option<ErasedMemo<'db>>;

fn sync_table(&self) -> &SyncTable;

/// Returns information about the current provisional status of `input`.
///
/// Is it a provisional value, a poisoned provisional memo, or has it been finalized and in
/// which iteration.
///
/// Returns `None` if `input` doesn't exist.
fn provisional_status<'db>(
&'db self,
zalsa: &'db Zalsa,
input: Id,
) -> Option<ProvisionalStatus<'db>>;
}

/// Function ingredients are the "workhorse" of salsa.
Expand Down Expand Up @@ -387,29 +366,6 @@ where
fn sync_table(&self) -> &SyncTable {
&self.sync_table
}

/// Returns `final` if the memo has the `verified_final` flag set.
///
/// Otherwise, the value is still provisional or the provisional memo has been poisoned. It
/// also returns the iteration in which this memo was created (always 0 except for cycle
/// heads).
fn provisional_status<'db>(
&'db self,
zalsa: &'db Zalsa,
input: Id,
) -> Option<ProvisionalStatus<'db>> {
let memo =
self.get_memo_from_table_for(zalsa, input, self.memo_ingredient_index(zalsa, input))?;

if memo.value.is_none() && memo.header.may_be_provisional() {
return Some(ProvisionalStatus::Poisoned {
iteration: memo.header.revisions.iteration(),
verified_at: memo.header.verified_at.load(),
});
}

Some(memo.header.provisional_status())
}
}

impl<C> Ingredient for IngredientImpl<C>
Expand Down Expand Up @@ -461,7 +417,7 @@ where
self,
zalsa,
self.database_key_index(id),
C::CYCLE_STRATEGY,
C::CYCLE_RECOVERY_STRATEGY,
flattened_input_outputs,
seen,
);
Expand Down
92 changes: 92 additions & 0 deletions src/function/cycle_strategy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use super::execute::{CyclePolicy, CycleStateImpl};
use super::fetch::{fetch_cold_cycle_panic, fetch_cold_cycle_recoverable_erased};
use super::memo::Memo;
use super::{ClaimGuard, Configuration, IngredientImpl};
use crate::DatabaseKeyIndex;
use crate::cycle::CycleRecoveryStrategy;
use crate::zalsa::{MemoIngredientIndex, Zalsa};
use crate::zalsa_local::ZalsaLocal;

pub struct Panic;
pub struct FallbackImmediate;
pub struct Fixpoint;

pub struct ExecuteContext<'db, C: Configuration> {
pub(super) ingredient: &'db IngredientImpl<C>,
pub(super) db: &'db C::DbView,
pub(super) claim_guard: ClaimGuard<'db>,
pub(super) opt_old_memo: Option<&'db Memo<C>>,
pub(super) memo_ingredient_index: MemoIngredientIndex,
}

pub type ExecuteResult<'db, C> = Option<&'db Memo<C>>;

pub struct FetchCycleContext<'db, C: Configuration> {
pub(super) ingredient: &'db IngredientImpl<C>,
pub(super) db: &'db C::DbView,
pub(super) zalsa: &'db Zalsa,
pub(super) zalsa_local: &'db ZalsaLocal,
pub(super) database_key_index: DatabaseKeyIndex,
pub(super) memo_ingredient_index: MemoIngredientIndex,
}

pub type FetchCycleResult<'db, C> = &'db Memo<C>;

pub trait CycleStrategy<C: Configuration>: 'static {
const RECOVERY_STRATEGY: CycleRecoveryStrategy = CycleRecoveryStrategy::Panic;

fn execute(context: ExecuteContext<'_, C>) -> ExecuteResult<'_, C>;

fn fetch_cold_cycle(context: FetchCycleContext<'_, C>) -> FetchCycleResult<'_, C>;
}

impl<C: Configuration> CycleStrategy<C> for Panic {
fn execute(context: ExecuteContext<'_, C>) -> ExecuteResult<'_, C> {
IngredientImpl::execute_panic(context)
}

fn fetch_cold_cycle(context: FetchCycleContext<'_, C>) -> FetchCycleResult<'_, C> {
fetch_cold_cycle_panic(context.zalsa_local, context.database_key_index)
}
}

impl<C: Configuration> CycleStrategy<C> for FallbackImmediate {
const RECOVERY_STRATEGY: CycleRecoveryStrategy = CycleRecoveryStrategy::FallbackImmediate;

fn execute(context: ExecuteContext<'_, C>) -> ExecuteResult<'_, C> {
IngredientImpl::execute_cycle(context, CyclePolicy::FallbackImmediate)
}

fn fetch_cold_cycle(context: FetchCycleContext<'_, C>) -> FetchCycleResult<'_, C> {
fetch_cold_cycle_recoverable(context)
}
}

impl<C: Configuration> CycleStrategy<C> for Fixpoint {
const RECOVERY_STRATEGY: CycleRecoveryStrategy = CycleRecoveryStrategy::Fixpoint;

fn execute(context: ExecuteContext<'_, C>) -> ExecuteResult<'_, C> {
IngredientImpl::execute_cycle(context, CyclePolicy::Fixpoint)
}

fn fetch_cold_cycle(context: FetchCycleContext<'_, C>) -> FetchCycleResult<'_, C> {
fetch_cold_cycle_recoverable(context)
}
}

fn fetch_cold_cycle_recoverable<C: Configuration>(
context: FetchCycleContext<'_, C>,
) -> FetchCycleResult<'_, C> {
let mut state = CycleStateImpl::new(
context.ingredient,
context.db,
context.memo_ingredient_index,
);
fetch_cold_cycle_recoverable_erased(
&mut state,
context.ingredient,
context.zalsa,
context.database_key_index,
)
.downcast::<C>()
}
Loading
Loading