/// Charges a canister (in instructions) for system API call overhead (exit,
/// accessing state, etc) and for using `num_bytes` bytes of memory. If
/// the canister has run out instructions or there are unexpected bugs, return
/// an error.
///
/// There are a number of scenarios that this function must handle where due
/// to potential bugs, the expected information is not available. In more
/// classical systems, we could just panic in such cases. However, for us
/// that has the danger of putting the subnet in a crash loop. So instead,
/// we emit a error log message and continue execution. We intentionally do
/// not introduce new error types in these paths as these error paths should
/// be extremely rare and we do not want to increase the complexity of the
/// code to handle hypothetical bugs.
Charges a canister (in instructions) for system API call overhead (exit,
accessing state, etc) and for using `num_bytes` bytes of memory. If
the canister has run out instructions or there are unexpected bugs, return
an error.

There are a number of scenarios that this function must handle where due
to potential bugs, the expected information is not available. In more
classical systems, we could just panic in such cases. However, for us
that has the danger of putting the subnet in a crash loop. So instead,
we emit a error log message and continue execution. We intentionally do
not introduce new error types in these paths as these error paths should
be extremely rare and we do not want to increase the complexity of the
code to handle hypothetical bugs.
Charges a canister ( in instructions ) for system API call overhead ( exit accessing state etc ) and for using ` num_bytes ` bytes of memory . If the canister has run out instructions or there are unexpected bugs return an error . There are a number of scenarios that this function must handle where due to potential bugs the expected information is not available . In more classical systems we could just panic in such cases . However for us that has the danger of putting the subnet in a crash loop . So instead we emit a error log message and continue execution . We intentionally do not introduce new error types in these paths as these error paths should be extremely rare and we do not want to increase the complexity of the code to handle hypothetical bugs .
fn charge_for_system_api_call<S: SystemApi>(
    log: &ReplicaLogger,
    canister_id: CanisterId,
    mut caller: &mut Caller<'_, StoreData<S>>,
    system_api_charge: NumInstructions,
    num_bytes: u32,
) -> Result<(), Trap> {
    let num_instructions_global = match caller.data().num_instructions_global {
        None => {
            error!(
                log,
                "[EXC-BUG] Canister {}: instructions counter is set to None.", canister_id,
            );
            return Err(process_err(
                caller,
                HypervisorError::InstructionLimitExceeded,
            ));
        }
        Some(global) => global,
    };
    match num_instructions_global.get(&mut caller) {
        Val::I64(current_instructions) => {
            let fee = caller
                .as_context_mut()
                .data_mut()
                .system_api
                .get_num_instructions_from_bytes(NumBytes::from(num_bytes as u64))
                .get() as i64
                + system_api_charge.get() as i64;
            if current_instructions < fee {
                info!(
                    log,
                    "Canister {}: ran out of instructions.  Current {}, fee {}",
                    canister_id,
                    current_instructions,
                    fee
                );
                return Err(process_err(
                    caller,
                    HypervisorError::InstructionLimitExceeded,
                ));
            }
            let updated_instructions = current_instructions - fee;
            if let Err(err) =
                num_instructions_global.set(&mut caller, Val::I64(updated_instructions))
            {
                error!(
                    log,
                    "[EXC-BUG] Canister {}: Setting instructions from {} to {} failed with {}",
                    canister_id,
                    current_instructions,
                    updated_instructions,
                    err
                );
                return Err(process_err(
                    caller,
                    HypervisorError::InstructionLimitExceeded,
                ));
            }
            Ok(())
        }
        others => {
            error!(
                log,
                "[EXC-BUG] Canister {}: expected value of type I64 instead got {:?}",
                canister_id,
                others,
            );
            Err(process_err(
                caller,
                HypervisorError::InstructionLimitExceeded,
            ))
        }
    }
}
fn charge_for_system_api_call < S : SystemApi > ( log : & ReplicaLogger , canister_id : CanisterId , mut caller : & mut Caller < ' _ , StoreData < S > > , system_api_charge : NumInstructions , num_bytes : u32 , ) -> Result < ( ) , Trap > { let num_instructions_global = match caller . data ( ) . num_instructions_global { None => { error ! ( log " " canister_id ) ; return Err ( process_err ( caller , HypervisorError :: InstructionLimitExceeded , ) ) ; } Some ( global ) => global , } ; match num_instructions_global . get ( & mut caller ) { Val :: I64 ( current_instructions ) => { let fee = caller . as_context_mut ( ) . data_mut ( ) . system_api . get_num_instructions_from_bytes ( NumBytes :: from ( num_bytes as u64 ) ) . get ( ) as i64 + system_api_charge . get ( ) as i64 ; if current_instructions < fee { info ! ( log " " canister_id current_instructions fee ) ; return Err ( process_err ( caller , HypervisorError :: InstructionLimitExceeded , ) ) ; } let updated_instructions = current_instructions - fee ; if let Err ( err ) = num_instructions_global . set ( & mut caller , Val :: I64 ( updated_instructions ) ) { error ! ( log " " canister_id current_instructions updated_instructions err ) ; return Err ( process_err ( caller , HypervisorError :: InstructionLimitExceeded , ) ) ; } Ok ( ( ) ) } others => { error ! ( log " " canister_id others ) ; Err ( process_err ( caller , HypervisorError :: InstructionLimitExceeded , ) ) } } }
Charges a canister (in instructions) for system API call overhead (exit,
accessing state, etc) and for using `num_bytes` bytes of memory.
Charges a canister ( in instructions ) for system API call overhead ( exit accessing state etc ) and for using ` num_bytes ` bytes of memory .
log
&ReplicaLogger
canister_id
CanisterId
caller
&mut Caller<'_, StoreData<S>>
system_api_charge
NumInstructions
num_bytes
u32