Skip to content
Open
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
31 changes: 30 additions & 1 deletion ic-canister-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,41 @@ impl From<CandidDecodeFailed> for IcError {
/// [`http_canister`]: https://github.com/dfinity/canhttp/tree/main/examples/http_canister/
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
pub struct IcRuntime {
_private: (),
allow_calls_when_stopping: bool,
}

impl IcRuntime {
/// Create a new instance of [`IcRuntime`].
pub fn new() -> Self {
Self::default()
}

/// Allow inter-canister calls when the canister is stopping.
///
/// <div class="warning">
/// Allowing inter-canister calls when the canister making the calls is stopping
/// could prevent that canister from being stopped and therefore upgraded.
/// This is because the stopping state does not prevent the canister itself from issuing
/// new calls (see the specification on <a href="https://docs.internetcomputer.org/references/ic-interface-spec#ic-stop_canister">stop_canister</a>).
/// </div>
pub fn allow_calls_when_stopping(mut self, allow: bool) -> Self {
self.allow_calls_when_stopping = allow;
self
}

fn ensure_allowed_to_make_call(&self) -> Result<(), IcError> {
if !self.allow_calls_when_stopping {
use ic_cdk::api::CanisterStatusCode;

return match ic_cdk::api::canister_status() {
CanisterStatusCode::Running => Ok(()),
CanisterStatusCode::Stopping
| CanisterStatusCode::Stopped
| CanisterStatusCode::Unrecognized(_) => Err(IcError::CallPerformFailed),
};
}
Ok(())
}
}

#[async_trait]
Expand All @@ -168,6 +195,7 @@ impl Runtime for IcRuntime {
In: ArgumentEncoder + Send,
Out: CandidType + DeserializeOwned,
{
self.ensure_allowed_to_make_call()?;
Call::unbounded_wait(id, method)
.with_args(&args)
.with_cycles(cycles)
Expand All @@ -186,6 +214,7 @@ impl Runtime for IcRuntime {
In: ArgumentEncoder + Send,
Out: CandidType + DeserializeOwned,
{
self.ensure_allowed_to_make_call()?;
Call::unbounded_wait(id, method)
.with_args(&args)
.await
Expand Down