Skip to content

Commit

Permalink
feat: Suspend support in spawn syscalls
Browse files Browse the repository at this point in the history
  • Loading branch information
xxuejie committed May 31, 2023
1 parent fdee47b commit 80b3d21
Show file tree
Hide file tree
Showing 6 changed files with 594 additions and 215 deletions.
2 changes: 1 addition & 1 deletion script/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod load_script_hash;
mod load_tx;
mod load_witness;
mod set_content;
mod spawn;
pub(crate) mod spawn;
mod utils;
mod vm_version;

Expand Down
195 changes: 140 additions & 55 deletions script/src/syscalls/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::syscalls::{
SPAWN_EXTRA_CYCLES_PER_MEMORY_PAGE, SPAWN_MAX_CONTENT_LENGTH, SPAWN_MAX_MEMORY,
SPAWN_MAX_PEAK_MEMORY, SPAWN_MEMORY_PAGE_SIZE, SPAWN_WRONG_MEMORY_LIMIT, WRONG_FORMAT,
};
use crate::types::{set_vm_max_cycles, CoreMachineType, Machine};
use crate::types::{
set_vm_max_cycles, CoreMachineType, Machine, MachineContext, ResumableMachine, ResumeData,
};
use crate::TransactionScriptsSyscallsGenerator;
use crate::{ScriptGroup, ScriptVersion};
use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider};
Expand All @@ -23,6 +25,7 @@ pub struct Spawn<DL> {
script_version: ScriptVersion,
syscalls_generator: TransactionScriptsSyscallsGenerator<DL>,
peak_memory: u64,
context: Arc<Mutex<MachineContext>>,
}

impl<DL: CellDataProvider + Clone + HeaderProvider + Send + Sync + 'static> Spawn<DL> {
Expand All @@ -31,12 +34,14 @@ impl<DL: CellDataProvider + Clone + HeaderProvider + Send + Sync + 'static> Spaw
script_version: ScriptVersion,
syscalls_generator: TransactionScriptsSyscallsGenerator<DL>,
peak_memory: u64,
context: Arc<Mutex<MachineContext>>,
) -> Self {
Self {
script_group,
script_version,
syscalls_generator,
peak_memory,
context,
}
}

Expand Down Expand Up @@ -142,47 +147,23 @@ where
return Ok(true);
}
// Build child machine.
let machine_content = Arc::new(Mutex::new(Vec::<u8>::new()));
let mut machine_child = {
let machine_isa = self.script_version.vm_isa();
let machine_version = self.script_version.vm_version();
let machine_core = CoreMachineType::new_with_memory(
machine_isa,
machine_version,
cycles_limit,
(memory_limit * SPAWN_MEMORY_PAGE_SIZE) as usize,
);
let machine_builder = DefaultMachineBuilder::new(machine_core)
.instruction_cycle_func(Box::new(estimate_cycles));
let machine_syscalls = self
.syscalls_generator
.generate_same_syscalls(self.script_version, &self.script_group);
let machine_builder = machine_syscalls
.into_iter()
.fold(machine_builder, |builder, syscall| builder.syscall(syscall));
let machine_builder = machine_builder.syscall(Box::new(
self.syscalls_generator.build_get_memory_limit(memory_limit),
));
let machine_builder = machine_builder.syscall(Box::new(
self.syscalls_generator
.build_set_content(Arc::clone(&machine_content), content_length.to_u64()),
));
let machine_builder =
machine_builder.syscall(Box::new(self.syscalls_generator.build_spawn(
self.script_version,
&self.script_group,
self.peak_memory + memory_limit,
)));
let mut machine_child = Machine::new(machine_builder.build());
set_vm_max_cycles(&mut machine_child, cycles_limit);
machine_child
let resume_data = ResumeData::Spawn {
callee_peak_memory: self.peak_memory + memory_limit,
callee_memory_limit: memory_limit,
content: Arc::new(Mutex::new(Vec::<u8>::new())),
content_length: content_length.to_u64(),
caller_exit_code_addr: exit_code_addr.to_u64(),
caller_content_addr: content_addr.to_u64(),
caller_content_length_addr: content_length_addr.to_u64(),
};

// Deduct cycles used to build the machine
let extra_cycles =
SPAWN_EXTRA_CYCLES_BASE + memory_limit * SPAWN_EXTRA_CYCLES_PER_MEMORY_PAGE;
machine_child.machine.add_cycles_no_checking(extra_cycles)?;

let mut machine_child = build_child_machine(
&self.script_group,
self.script_version,
&self.syscalls_generator,
cycles_limit,
&resume_data,
&self.context,
)?;
// Get binary.
let program = {
let cell = self.fetch_cell(source, index as usize);
Expand Down Expand Up @@ -225,7 +206,7 @@ where
argv_vec.push(cstr);
addr += 8;
}
// Run child machine.
// Load program into child machine.
match machine_child.load_program(&program, &argv_vec) {
Ok(size) => {
machine_child
Expand All @@ -237,24 +218,128 @@ where
return Ok(true);
}
}
// Check result.
// Deduct cycles used to build the child machine
let extra_cycles =
SPAWN_EXTRA_CYCLES_BASE + memory_limit * SPAWN_EXTRA_CYCLES_PER_MEMORY_PAGE;
machine_child.machine.add_cycles_no_checking(extra_cycles)?;
// Run the child machine and check result.
match machine_child.run() {
Ok(data) => {
machine.set_register(A0, Mac::REG::from_u32(0));
machine
.memory_mut()
.store8(&exit_code_addr, &Mac::REG::from_i8(data))?;
machine
.memory_mut()
.store_bytes(content_addr.to_u64(), &machine_content.lock().unwrap())?;
machine.memory_mut().store64(
&content_length_addr,
&Mac::REG::from_u64(machine_content.lock().unwrap().len() as u64),
)?;
machine.add_cycles_no_checking(machine_child.machine.cycles())?;
update_caller_machine(machine, data, machine_child.machine.cycles(), &resume_data)?;
Ok(true)
}
Err(VMError::CyclesExceeded) => {
let mut context = self
.context
.lock()
.map_err(|e| VMError::Unexpected(format!("Failed to acquire lock: {}", e)))?;
context
.suspended_machines
.push(ResumableMachine::new(machine_child, resume_data));
Err(VMError::CyclesExceeded)
}
Err(err) => Err(err),
}
}
}

pub fn build_child_machine<
DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
>(
script_group: &ScriptGroup,
script_version: ScriptVersion,
syscalls_generator: &TransactionScriptsSyscallsGenerator<DL>,
cycles_limit: u64,
resume_data: &ResumeData,
context: &Arc<Mutex<MachineContext>>,
) -> Result<Machine, VMError> {
let (callee_peak_memory, callee_memory_limit, content, content_length) = match resume_data {
ResumeData::Spawn {
callee_peak_memory,
callee_memory_limit,
content,
content_length,
..
} => (
*callee_peak_memory,
*callee_memory_limit,
content,
*content_length,
),
_ => {
return Err(VMError::Unexpected(
"Building child machine requires Spawn variant of ResumeData!".to_string(),
))
}
};

let machine_isa = script_version.vm_isa();
let machine_version = script_version.vm_version();
let machine_core = CoreMachineType::new_with_memory(
machine_isa,
machine_version,
cycles_limit,
(callee_memory_limit * SPAWN_MEMORY_PAGE_SIZE) as usize,
);
let machine_builder =
DefaultMachineBuilder::new(machine_core).instruction_cycle_func(Box::new(estimate_cycles));
let machine_syscalls = syscalls_generator.generate_same_syscalls(script_version, script_group);
let machine_builder = machine_syscalls
.into_iter()
.fold(machine_builder, |builder, syscall| builder.syscall(syscall));
let machine_builder = machine_builder.syscall(Box::new(
syscalls_generator.build_get_memory_limit(callee_memory_limit),
));
let machine_builder = machine_builder.syscall(Box::new(
syscalls_generator.build_set_content(Arc::clone(content), content_length),
));
let machine_builder = machine_builder.syscall(Box::new(syscalls_generator.build_spawn(
script_version,
script_group,
callee_peak_memory,
context.clone(),
)));
let mut machine_child = Machine::new(machine_builder.build());
set_vm_max_cycles(&mut machine_child, cycles_limit);
Ok(machine_child)
}

pub fn update_caller_machine<Mac: SupportMachine>(
caller: &mut Mac,
callee_exit_code: i8,
callee_cycles: u64,
resume_data: &ResumeData,
) -> Result<(), VMError> {
let (content, caller_exit_code_addr, caller_content_addr, caller_content_length_addr) =
match resume_data {
ResumeData::Spawn {
content,
caller_exit_code_addr,
caller_content_addr,
caller_content_length_addr,
..
} => (
content,
*caller_exit_code_addr,
*caller_content_addr,
*caller_content_length_addr,
),
_ => return Ok(()),
};

caller.set_register(A0, Mac::REG::from_u32(0));
caller.memory_mut().store8(
&Mac::REG::from_u64(caller_exit_code_addr),
&Mac::REG::from_i8(callee_exit_code),
)?;
caller
.memory_mut()
.store_bytes(caller_content_addr.to_u64(), &content.lock().unwrap())?;
caller.memory_mut().store64(
&Mac::REG::from_u64(caller_content_length_addr),
&Mac::REG::from_u64(content.lock().unwrap().len() as u64),
)?;
caller.add_cycles_no_checking(callee_cycles)?;

Ok(())
}
Loading

0 comments on commit 80b3d21

Please sign in to comment.