Skip to content
Open
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
12 changes: 9 additions & 3 deletions ic-cdk-bindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub struct Config {
canister_name: String,
candid_path: PathBuf,
mode: Mode,
type_selector_config_path: Option<PathBuf>, // TODO: Implement type selector config
type_selector_config_path: Option<PathBuf>,
}

/// Bindgen mode.
Expand Down Expand Up @@ -155,8 +155,14 @@ impl Config {
e
)
});
// unused are not handled
let (output, _unused) = emit_bindgen(&rust_bindgen_config, &env, &actor, &prog);
let (output, unused) = emit_bindgen(&rust_bindgen_config, &env, &actor, &prog);
if !unused.is_empty() {
println!(
"cargo:warning=ic-cdk-bindgen: {} unused type(s) were not emitted: {:?}",
unused.len(),
unused
);
}

// 2. Generate the Rust bindings using the Handlebars template
let mut external = ExternalConfig::default();
Expand Down
19 changes: 16 additions & 3 deletions ic-cdk-timers/src/global_timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ fn reschedule_timer(timers: &mut BinaryHeap<Timer>, id: TaskId, base: u64, inter
task: id,
time,
counter: state::next_counter(),
backoff_secs: 0,
});
}
None => ic0::debug_print(
Expand Down Expand Up @@ -49,7 +50,7 @@ extern "C" fn global_timer() {
first = false;
} else if insn_count == 0 {
insn_count = ic0::performance_counter(0);
} else if insn_count * 3 + ic0::performance_counter(0) > 40_000_000_000 {
} else if insn_count.saturating_mul(3).saturating_add(ic0::performance_counter(0)) > 40_000_000_000 {
ic0::debug_print(
b"[ic-cdk-timers] canister_global_timer: approaching instruction limit, \
deferring remaining timers to next round",
Expand Down Expand Up @@ -143,7 +144,7 @@ fn do_timer(
// here is performing an inter-canister call to ourselves; traps will be caught at the call
// boundary. This invokes a meaningful cycles cost, and should an alternative for catching traps
// become available, this code should be rewritten.
let env = Box::new(CallEnv {
let mut env = Box::new(CallEnv {
timer,
method_handle: ic_cdk_executor::extend_current_method_context(),
});
Expand All @@ -153,7 +154,19 @@ fn do_timer(
let cost = ic0::cost_call(METHOD_NAME.len() as u64, 8);
// --- no allocations between the liquid cycles check and call_perform
if liquid_cycles < cost {
ic0::debug_print(b"[ic-cdk-timers] unable to schedule timer: not enough liquid cycles");
let next_backoff_secs = match env.timer.backoff_secs {
0 => 5,
n => (n * 2).min(60),
};
ic0::debug_print(
format!(
"[ic-cdk-timers] unable to schedule timer: not enough liquid cycles, \
retrying in {next_backoff_secs}s"
)
.as_bytes(),
);
env.timer.time = now.saturating_add(next_backoff_secs as u64 * 1_000_000_000);
env.timer.backoff_secs = next_backoff_secs;
return ControlFlow::Break(Some(env.timer));
}
let env = Box::<CallEnv>::into_raw(env) as usize;
Expand Down
3 changes: 3 additions & 0 deletions ic-cdk-timers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub fn set_timer(delay: Duration, future: impl Future<Output = ()> + 'static) ->
task: key,
time: scheduled_time,
counter: state::next_counter(),
backoff_secs: 0,
})
});
state::update_ic0_timer();
Expand Down Expand Up @@ -127,6 +128,7 @@ where
task: key,
time: scheduled_time,
counter: state::next_counter(),
backoff_secs: 0,
});
});
state::update_ic0_timer();
Expand Down Expand Up @@ -177,6 +179,7 @@ pub fn set_timer_interval_serial(interval: Duration, func: impl AsyncFnMut() + '
task: key,
time: scheduled_time,
counter: state::next_counter(),
backoff_secs: 0,
});
});
state::update_ic0_timer();
Expand Down
1 change: 1 addition & 0 deletions ic-cdk-timers/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub(crate) struct Timer {
pub(crate) task: TaskId,
pub(crate) time: u64,
pub(crate) counter: u128,
pub(crate) backoff_secs: u32,
}

// Timers are sorted first by time, then by insertion order to ensure deterministic ordering.
Expand Down
23 changes: 20 additions & 3 deletions ic-cdk/src/stable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ impl<M: StableMemory> StableIO<M> {
///
/// When it cannot grow the memory to accommodate the new data.
pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
let required_capacity_bytes = self.offset + buf.len() as u64;
let required_capacity_bytes = self
.offset
.checked_add(buf.len() as u64)
.ok_or(StableMemoryError::OutOfBounds)?;
let required_capacity_pages = required_capacity_bytes.div_ceil(WASM_PAGE_SIZE_IN_BYTES);
let current_pages = self.capacity;
let additional_pages_required = required_capacity_pages.saturating_sub(current_pages);
Expand Down Expand Up @@ -233,9 +236,23 @@ impl<M: StableMemory> StableIO<M> {
self.offset = match offset {
io::SeekFrom::Start(offset) => offset,
io::SeekFrom::End(offset) => {
((self.capacity * WASM_PAGE_SIZE_IN_BYTES) as i64 + offset) as u64
let end = i64::try_from(self.capacity * WASM_PAGE_SIZE_IN_BYTES)
.ok()
.and_then(|end| end.checked_add(offset))
.and_then(|pos| u64::try_from(pos).ok());
end.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "seek position overflow")
})?
}
io::SeekFrom::Current(offset) => {
let pos = i64::try_from(self.offset)
.ok()
.and_then(|cur| cur.checked_add(offset))
.and_then(|pos| u64::try_from(pos).ok());
pos.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "seek position overflow")
})?
}
io::SeekFrom::Current(offset) => (self.offset as i64 + offset) as u64,
};

Ok(self.offset)
Expand Down
Loading