Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: CallError redesign #548

Merged
merged 12 commits into from
Jan 16, 2025
30 changes: 7 additions & 23 deletions ic-cdk-timers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use std::{
use futures::{stream::FuturesUnordered, StreamExt};
use slotmap::{new_key_type, KeyData, SlotMap};

use ic_cdk::call::{Call, CallError, CallPerformErrorCode, RejectCode, SendableCall};
use ic_cdk::call::{Call, RejectCode, SendableCall};

// To ensure that tasks are removable seamlessly, there are two separate concepts here: tasks, for the actual function being called,
// and timers, the scheduled execution of tasks. As this is an implementation detail, this does not affect the exported name TimerId,
Expand Down Expand Up @@ -117,8 +117,8 @@ extern "C" fn global_timer() {
ic_cdk::api::canister_self(),
"<ic-cdk internal> timer_executor",
)
.with_arg(task_id.0.as_ffi())
.call::<()>()
.with_raw_args(task_id.0.as_ffi().to_be_bytes().to_vec())
.call_raw()
mraszyk marked this conversation as resolved.
Show resolved Hide resolved
.await,
)
});
Expand All @@ -134,23 +134,7 @@ extern "C" fn global_timer() {
let task_id = timer.task;
if let Err(e) = res {
ic_cdk::println!("[ic-cdk-timers] canister_global_timer: {e:?}");
let mut retry_later = false;
match e {
CallError::CallRejected(reject_code, _) => {
if reject_code == RejectCode::SysTransient {
retry_later = true;
}
}
CallError::CallPerformFailed(call_perform_error_code) => {
if call_perform_error_code == CallPerformErrorCode::SysTransient {
retry_later = true;
}
}
CallError::CandidEncodeFailed(_) | CallError::CandidDecodeFailed(_) => {
// These errors are not transient, and will not be retried.
}
}
if retry_later {
if e.reject_code() == RejectCode::SysTransient {
// Try to execute the timer again later.
TIMERS.with(|timers| {
timers.borrow_mut().push(timer);
Expand Down Expand Up @@ -275,15 +259,15 @@ fn update_ic0_timer() {
export_name = "canister_update_ic_cdk_internal.timer_executor"
)]
extern "C" fn timer_executor() {
use candid::utils::{decode_one, encode_one};
if ic_cdk::api::msg_caller() != ic_cdk::api::canister_self() {
ic_cdk::trap("This function is internal to ic-cdk and should not be called externally.");
}
let arg_bytes = ic_cdk::api::msg_arg_data();
// timer_executor is only called by the canister itself (from global_timer),
// so we can safely assume that the argument is a valid TimerId (u64).
// And we don't need decode_one_with_config/DecoderConfig to defense against malicious payload.
let task_id: u64 = decode_one(&arg_bytes).unwrap();
assert!(arg_bytes.len() == 8);
let task_id = u64::from_be_bytes(arg_bytes.try_into().unwrap());

let task_id = TimerId(KeyData::from_ffi(task_id));
// We can't be holding `TASKS` when we call the function, because it may want to schedule more tasks.
Expand All @@ -304,5 +288,5 @@ extern "C" fn timer_executor() {
}
}
}
ic_cdk::api::msg_reply(encode_one(()).unwrap());
ic_cdk::api::msg_reply([]);
}
Loading