diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 52fe94aae..84bafc1a1 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -486,9 +486,20 @@ impl Host { self.as_budget().with_shadow_mode(f, e) } + /// Returns whether the Host can be finished by calling + /// [`Host::try_finish`]. + /// + /// Returns true if the host reference is unique, refcount = 1. + pub fn can_finish(&self) -> bool { + Rc::strong_count(&self.0) == 1 + } + /// Accept a _unique_ (refcount = 1) host reference and destroy the /// underlying [`HostImpl`], returning its finalized components containing /// processing side effects to the caller as a tuple wrapped in `Ok(...)`. + /// + /// Use [`Host::can_finish`] to determine before calling the function if it + /// will succeed. pub fn try_finish(self) -> Result<(Storage, Events), HostError> { let events = self.try_borrow_events()?.externalize(&self)?; Rc::try_unwrap(self.0) diff --git a/soroban-env-host/src/test.rs b/soroban-env-host/src/test.rs index 5bc8bf861..6b93b6a36 100644 --- a/soroban-env-host/src/test.rs +++ b/soroban-env-host/src/test.rs @@ -24,4 +24,5 @@ mod symbol; mod tuple; mod vec; +mod finish; mod metering_benchmark; diff --git a/soroban-env-host/src/test/finish.rs b/soroban-env-host/src/test/finish.rs new file mode 100644 index 000000000..96dd99546 --- /dev/null +++ b/soroban-env-host/src/test/finish.rs @@ -0,0 +1,31 @@ +use crate::Host; + +#[test] +fn can_finish_on_one_reference() { + let host1 = Host::test_host_with_recording_footprint(); + assert!(host1.can_finish()); + assert!(host1.try_finish().is_ok()); +} + +#[test] +fn cant_finish_on_multiple_reference() { + let host1 = Host::test_host_with_recording_footprint(); + let host2 = host1.clone(); + assert!(!host1.can_finish()); + assert!(!host2.can_finish()); + assert!(host1.try_finish().is_err()); + _ = host2; +} + +#[test] +fn can_finish_on_reduced_to_one_reference_count() { + let host1 = Host::test_host_with_recording_footprint(); + { + let host2 = host1.clone(); + assert!(!host1.can_finish()); + assert!(!host2.can_finish()); + _ = host1; + } + assert!(host1.can_finish()); + assert!(host1.try_finish().is_ok()); +}