Skip to content

Commit

Permalink
refactor: Consolidate how system state modifications are extracted fr…
Browse files Browse the repository at this point in the history
…om the System API (#3706)

Currently, there are two similar functions in the `SystemApiImpl` to
extract system state modifications. One of them
`take_system_state_modifications` is used in the sandbox when preparing
the state changes to be transmitted back to the replica. Then, the
replica after deserializing the result that it receives from the sandbox
calls `into_system_state_changes` on the reconstructed `system_api` to
extract the changes again.

In a recent [PR](#363),
`into_system_state_modifications` was changed (to make it more clear
which changes are relevant per message type) but
`take_system_state_modifications` wasn't. This works correctly because
`into_system_state_modifications` is the last one that's called before
applying the state changes back to the canister state. However, it's
also a very clear example of how the code can easily diverge and go
unnoticed, potentially with more severe implications in the future.

This PR proposes to use one function which seems to provide the usual
benefits of consistent way of applying the changes and "having one way"
of doing the same task. We keep `take_system_state_modifications` (this
allows us to get rid of some `clone`s but more importantly it's needed
in the sandbox, see comment in the existing code) and change the call
sites respectively.
  • Loading branch information
dsarlis authored Jan 31, 2025
1 parent eb4a6d5 commit cde7071
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 50 deletions.
2 changes: 1 addition & 1 deletion rs/canister_sandbox/src/sandbox_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ impl Execution {
.system_api_mut()
.expect("System api not present in the wasmtime instance")
.take_system_state_modifications(),
Err(system_api) => system_api.into_system_state_modifications(),
Err(mut system_api) => system_api.take_system_state_modifications(),
};

let execution_state_modifications = deltas.map(
Expand Down
4 changes: 2 additions & 2 deletions rs/embedders/src/wasm_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,11 @@ impl WasmExecutor for WasmExecutorImpl {
}),
None => None,
};
let system_api = match instance_or_system_api {
let mut system_api = match instance_or_system_api {
Ok(instance) => instance.into_store_data().system_api.unwrap(),
Err(system_api) => system_api,
};
let system_state_modifications = system_api.into_system_state_modifications();
let system_state_modifications = system_api.take_system_state_modifications();

(
compilation_result,
Expand Down
50 changes: 10 additions & 40 deletions rs/system_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1494,7 +1494,8 @@ impl SystemApiImpl {
}
}

pub fn into_system_state_modifications(self) -> SystemStateModifications {
pub fn take_system_state_modifications(&mut self) -> SystemStateModifications {
let system_state_modifications = self.sandbox_safe_system_state.take_changes();
match self.api_type {
// List all fields of `SystemStateModifications` so that
// there's an explicit decision that needs to be made
Expand All @@ -1514,53 +1515,28 @@ impl SystemApiImpl {
},
ApiType::NonReplicatedQuery { .. } => SystemStateModifications {
new_certified_data: None,
callback_updates: self
.sandbox_safe_system_state
.system_state_modifications
.callback_updates
.clone(),
callback_updates: system_state_modifications.callback_updates,
cycles_balance_change: CyclesBalanceChange::zero(),
reserved_cycles: Cycles::zero(),
consumed_cycles_by_use_case: BTreeMap::new(),
call_context_balance_taken: None,
request_slots_used: self
.sandbox_safe_system_state
.system_state_modifications
.request_slots_used
.clone(),
requests: self
.sandbox_safe_system_state
.system_state_modifications
.requests
.clone(),
request_slots_used: system_state_modifications.request_slots_used,
requests: system_state_modifications.requests,
new_global_timer: None,
canister_log: Default::default(),
on_low_wasm_memory_hook_condition_check_result: None,
},
ApiType::ReplicatedQuery { .. } => SystemStateModifications {
new_certified_data: None,
callback_updates: vec![],
cycles_balance_change: self
.sandbox_safe_system_state
.system_state_modifications
.cycles_balance_change,
cycles_balance_change: system_state_modifications.cycles_balance_change,
reserved_cycles: Cycles::zero(),
consumed_cycles_by_use_case: self
.sandbox_safe_system_state
.system_state_modifications
.consumed_cycles_by_use_case,
call_context_balance_taken: self
.sandbox_safe_system_state
.system_state_modifications
.call_context_balance_taken,
consumed_cycles_by_use_case: system_state_modifications.consumed_cycles_by_use_case,
call_context_balance_taken: system_state_modifications.call_context_balance_taken,
request_slots_used: BTreeMap::new(),
requests: vec![],
new_global_timer: None,
canister_log: self
.sandbox_safe_system_state
.system_state_modifications
.canister_log
.clone(),
canister_log: system_state_modifications.canister_log,
on_low_wasm_memory_hook_condition_check_result: None,
},
ApiType::Start { .. }
Expand All @@ -1570,16 +1546,10 @@ impl SystemApiImpl {
| ApiType::Update { .. }
| ApiType::Cleanup { .. }
| ApiType::ReplyCallback { .. }
| ApiType::RejectCallback { .. } => {
self.sandbox_safe_system_state.system_state_modifications
}
| ApiType::RejectCallback { .. } => system_state_modifications,
}
}

pub fn take_system_state_modifications(&mut self) -> SystemStateModifications {
self.sandbox_safe_system_state.take_changes()
}

pub fn stable_memory_size(&self) -> NumWasmPages {
self.stable_memory().stable_memory_size
}
Expand Down
2 changes: 1 addition & 1 deletion rs/system_api/tests/sandbox_safe_system_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ fn call_increases_cycles_consumed_metric() {
api.ic0_call_new(0, 0, 0, 0, 0, 0, 0, 0, &[]).unwrap();
api.ic0_call_perform().unwrap();

let system_state_modifications = api.into_system_state_modifications();
let system_state_modifications = api.take_system_state_modifications();
system_state_modifications
.apply_changes(
UNIX_EPOCH,
Expand Down
12 changes: 6 additions & 6 deletions rs/system_api/tests/system_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,7 @@ fn certified_data_set() {
// Copy the certified data into the system state.
api.ic0_certified_data_set(0, 32, &heap).unwrap();

let system_state_modifications = api.into_system_state_modifications();
let system_state_modifications = api.take_system_state_modifications();
system_state_modifications
.apply_changes(
UNIX_EPOCH,
Expand Down Expand Up @@ -1337,7 +1337,7 @@ fn call_perform_not_enough_cycles_does_not_trap() {
res
),
}
let system_state_modifications = api.into_system_state_modifications();
let system_state_modifications = api.take_system_state_modifications();
system_state_modifications
.apply_changes(
UNIX_EPOCH,
Expand Down Expand Up @@ -1481,7 +1481,7 @@ fn helper_test_on_low_wasm_memory(
.unwrap();
}

let system_state_modifications = api.into_system_state_modifications();
let system_state_modifications = api.take_system_state_modifications();
system_state_modifications
.apply_changes(
UNIX_EPOCH,
Expand Down Expand Up @@ -1750,7 +1750,7 @@ fn push_output_request_respects_memory_limits() {
);

// Ensure that exactly one output request was pushed.
let system_state_modifications = api.into_system_state_modifications();
let system_state_modifications = api.take_system_state_modifications();
system_state_modifications
.apply_changes(
UNIX_EPOCH,
Expand Down Expand Up @@ -1866,7 +1866,7 @@ fn push_output_request_oversized_request_memory_limits() {
);

// Ensure that exactly one output request was pushed.
let system_state_modifications = api.into_system_state_modifications();
let system_state_modifications = api.take_system_state_modifications();
system_state_modifications
.apply_changes(
UNIX_EPOCH,
Expand Down Expand Up @@ -1902,7 +1902,7 @@ fn ic0_global_timer_set_is_propagated_from_sandbox() {

// Propagate system state changes
assert_eq!(system_state.global_timer, CanisterTimer::Inactive);
let system_state_modifications = api.into_system_state_modifications();
let system_state_modifications = api.take_system_state_modifications();
system_state_modifications
.apply_changes(
UNIX_EPOCH,
Expand Down

0 comments on commit cde7071

Please sign in to comment.