Skip to content

Commit

Permalink
Remove error type bound on Handle::execute() via new variant `Execu…
Browse files Browse the repository at this point in the history
…teError::Handle`.

This is awkward because handle errors may also end up in the `Check`
variant, but in order to avoid that, we’d have to do one of these:

* Make `ExecuteError` take individual generics for its nested errors.
* Expose `TransactionInUniverse` and use its `Mismatch` type.
* Make `Transaction::check()` have an error variant for handle errors
  when they occur inside any transaction. (That might actually be a
  good idea, to distinguish “transient” from permanent errors.)

All of those are more complex, so we’ll stick with this for now.
  • Loading branch information
kpreid committed Jun 19, 2024
1 parent b0aae12 commit 54f8f92
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 13 deletions.
19 changes: 18 additions & 1 deletion all-is-cubes/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use alloc::sync::Arc;
use core::any::type_name;
use core::{fmt, mem};

use crate::universe::{Handle, UTransactional, UniverseTransaction};
use crate::universe::{Handle, HandleError, UTransactional, UniverseTransaction};
use crate::util::ErrorIfStd;

mod generic;
Expand Down Expand Up @@ -218,6 +218,19 @@ pub enum ExecuteError<Txn: Transaction = UniverseTransaction> {
/// See the documentation of [`Transaction::commit()`] for the unfortunate
/// implications of this.
Commit(CommitError),

/// Executing the transaction required accessing a [`Handle`] that was unavailable.
///
/// The [`HandleError`] will include the name of the problematic handle.
///
/// This error may be transient, and
/// unlike [`ExecuteError::Commit`], does not indicate data corruption,
/// but code which triggers it should generally be considered incorrect.
///
/// Note that this error is returned by [`Handle::execute()`], but transactions whose
/// `check` involves accessing handles will instead produce [`ExecuteError::Check`]s.
/// This may change in the future.
Handle(HandleError),
}

// Manual impl required to set proper associated type bounds.
Expand All @@ -230,6 +243,7 @@ where
Self::Merge(e) => Self::Merge(e.clone()),
Self::Check(e) => Self::Check(e.clone()),
Self::Commit(e) => Self::Commit(e.clone()),
Self::Handle(e) => Self::Handle(e.clone()),
}
}
}
Expand All @@ -244,6 +258,7 @@ crate::util::cfg_should_impl_error! {
ExecuteError::Merge(e) => e.source(),
ExecuteError::Check(e) => e.source(),
ExecuteError::Commit(e) => e.source(),
ExecuteError::Handle(e) => e.source(),
}
}
}
Expand All @@ -258,6 +273,7 @@ where
Self::Merge(e) => f.debug_tuple("Merge").field(e).finish(),
Self::Check(e) => f.debug_tuple("Check").field(e).finish(),
Self::Commit(e) => f.debug_tuple("Commit").field(e).finish(),
Self::Handle(e) => f.debug_tuple("Handle").field(e).finish(),
}
}
}
Expand All @@ -271,6 +287,7 @@ where
ExecuteError::Merge(e) => e.fmt(f),
ExecuteError::Check(e) => e.fmt(f),
ExecuteError::Commit(e) => e.fmt(f),
ExecuteError::Handle(e) => e.fmt(f),
}
}
}
Expand Down
18 changes: 6 additions & 12 deletions all-is-cubes/src/universe/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,33 +216,27 @@ impl<T: 'static> Handle<T> {

/// Execute the given transaction on the referent.
///
/// Returns an error if the transaction's preconditions were not met, if the
/// referent was already borrowed (which is denoted as an [`ExecuteError::Check`]),
/// or if the transaction encountered an unexpected error.
/// Returns an error if the transaction's preconditions are not met,
/// if the transaction encountered an internal error, or if the referent
/// was already being read or written (which is expressed as an
/// [`ExecuteError::Commit`], because it is a shouldn’t-happen kind of error).
#[inline(never)]
pub fn execute(
&self,
transaction: &<T as Transactional>::Transaction,
) -> Result<(), ExecuteError<<T as Transactional>::Transaction>>
where
T: Transactional,
// TODO: relax `Mismatch` bound and use a wrapper type instead, when custom error types are actually in use
//
// `Output = NoOutput` is required because, if there *were* outputs,
// they would need to be directed to some destination in the `Universe`,
// not the caller.
T::Transaction: Transaction<Mismatch = PreconditionFailed, Output = transaction::NoOutput>,
T::Transaction: Transaction<Output = transaction::NoOutput>,
{
let outcome: Result<
Result<(), ExecuteError<<T as Transactional>::Transaction>>,
HandleError,
> = self.try_modify(|data| transaction.execute(data, &mut transaction::no_outputs));
outcome.map_err(|_| {
ExecuteError::Check(PreconditionFailed {
location: "Handle::execute()",
problem: "target is currently in use",
})
})?
outcome.map_err(ExecuteError::Handle)?
}

fn upgrade(&self) -> Result<StrongEntryRef<T>, HandleError> {
Expand Down

0 comments on commit 54f8f92

Please sign in to comment.