diff --git a/script/src/v2_scheduler.rs b/script/src/v2_scheduler.rs
index 8efc260547a..3d66a9ee6c3 100644
--- a/script/src/v2_scheduler.rs
+++ b/script/src/v2_scheduler.rs
@@ -33,6 +33,7 @@ use std::{
};
const ROOT_VM_ID: VmId = FIRST_VM_ID;
+const MAX_VMS_COUNT: u64 = 16;
const MAX_INSTANTIATED_VMS: usize = 4;
/// A single Scheduler instance is used to verify a single script
@@ -53,6 +54,7 @@ where
script_version: ScriptVersion,
syscalls_generator: TransactionScriptsSyscallsGenerator
,
+ max_vms_count: u64,
total_cycles: Cycle,
next_vm_id: VmId,
next_pipe_slot: u64,
@@ -83,6 +85,7 @@ where
tx_data,
script_version,
syscalls_generator,
+ max_vms_count: MAX_VMS_COUNT,
total_cycles: 0,
next_vm_id: FIRST_VM_ID,
next_pipe_slot: FIRST_PIPE_SLOT,
@@ -112,6 +115,7 @@ where
tx_data,
script_version,
syscalls_generator,
+ max_vms_count: full.max_vms_count,
total_cycles: full.total_cycles,
next_vm_id: full.next_vm_id,
next_pipe_slot: full.next_pipe_slot,
@@ -135,6 +139,7 @@ where
/// Suspend current scheduler into a serializable full state
pub fn suspend(mut self) -> Result {
+ assert!(self.message_box.lock().expect("lock").is_empty());
let mut vms = Vec::with_capacity(self.states.len());
let instantiated_ids: Vec<_> = self.instantiated.keys().cloned().collect();
for id in instantiated_ids {
@@ -145,6 +150,7 @@ where
vms.push((id, state, snapshot));
}
Ok(FullSuspendedState {
+ max_vms_count: self.max_vms_count,
total_cycles: self.total_cycles,
next_vm_id: self.next_vm_id,
next_pipe_slot: self.next_pipe_slot,
@@ -313,7 +319,13 @@ where
if !pipes_valid {
continue;
}
- // TODO: spawn limits
+ if self.suspended.len() + self.instantiated.len() > self.max_vms_count as usize
+ {
+ self.ensure_vms_instantiated(&[vm_id])?;
+ let (_, machine) = self.instantiated.get_mut(&vm_id).unwrap();
+ machine.machine.set_register(A0, MAX_VMS_SPAWNED as u64);
+ continue;
+ }
let spawned_vm_id =
self.boot_vm(&args.data_piece_id, args.offset, args.length, &args.argv)?;
// Move passed pipes from spawner to spawnee
diff --git a/script/src/v2_syscalls.rs b/script/src/v2_syscalls.rs
index 5e17bd9c544..868de598689 100644
--- a/script/src/v2_syscalls.rs
+++ b/script/src/v2_syscalls.rs
@@ -43,4 +43,4 @@ where
pub fn set_base_cycles(&mut self, base_cycles: u64) {
*self.base_cycles.lock().expect("lock") = base_cycles;
}
-}
+}
\ No newline at end of file
diff --git a/script/src/v2_types.rs b/script/src/v2_types.rs
index 599dbe6abb1..42e5eb6a192 100644
--- a/script/src/v2_types.rs
+++ b/script/src/v2_types.rs
@@ -141,6 +141,7 @@ impl TryFrom<(u64, u64, u64)> for DataPieceId {
/// fully recover the running environment with the full transaction environment.
#[derive(Clone, Debug)]
pub struct FullSuspendedState {
+ pub max_vms_count: u64,
pub total_cycles: Cycle,
pub next_vm_id: VmId,
pub next_pipe_slot: u64,
diff --git a/script/src/verify/tests/ckb_latest/features_since_v2023.rs b/script/src/verify/tests/ckb_latest/features_since_v2023.rs
index 73c4f263a89..77df460c086 100644
--- a/script/src/verify/tests/ckb_latest/features_since_v2023.rs
+++ b/script/src/verify/tests/ckb_latest/features_since_v2023.rs
@@ -95,6 +95,12 @@ fn check_spawn_read_then_close() {
assert_eq!(result.is_ok(), SCRIPT_VERSION == ScriptVersion::V2);
}
+#[test]
+fn check_spawn_max_vms_count() {
+ let result = simple_spawn_test("testdata/spawn_cases", &[10]);
+ assert_eq!(result.is_ok(), SCRIPT_VERSION == ScriptVersion::V2);
+}
+
#[test]
fn check_vm_version() {
let script_version = SCRIPT_VERSION;
@@ -348,7 +354,7 @@ fn check_spawn_recursive() {
let result = verifier.verify(script_version, &rtx, 70_000_000);
if script_version >= ScriptVersion::V2 {
let msg = result.unwrap_err().to_string();
- assert!(msg.contains("ExceededMaximumCycles"))
+ assert!(msg.contains("error code 8"))
} else {
assert!(result.is_err())
}
diff --git a/script/testdata/spawn_cases b/script/testdata/spawn_cases
index 06ee2e2349c..b80d3cd078d 100755
Binary files a/script/testdata/spawn_cases and b/script/testdata/spawn_cases differ
diff --git a/script/testdata/spawn_cases.c b/script/testdata/spawn_cases.c
index 712cace0d0c..f43a4505b27 100644
--- a/script/testdata/spawn_cases.c
+++ b/script/testdata/spawn_cases.c
@@ -320,6 +320,20 @@ int child_read_then_close() {
return err;
}
+int parent_max_vms_count() {
+ const char* argv[2] = {"", 0};
+ return simple_spawn_args(0, 1, argv);
+}
+
+int child_max_vms_count() {
+ const char* argv[2] = {"", 0};
+ int err = simple_spawn_args(0, 1, argv);
+ CHECK2(err == 0 || err == CKB_MAX_VMS_SPAWNED, -2);
+ err = 0;
+exit:
+ return err;
+}
+
int parent_entry(int case_id) {
int err = 0;
uint64_t pid = 0;
@@ -341,6 +355,9 @@ int parent_entry(int case_id) {
err = parent_inherited_fds_without_owner(&pid);
} else if (case_id == 9) {
err = parent_read_then_close(&pid);
+ } else if (case_id == 10) {
+ err = parent_max_vms_count(&pid);
+ return err;
} else {
CHECK2(false, -2);
}
@@ -373,6 +390,8 @@ int child_entry(int case_id) {
return 0;
} else if (case_id == 9) {
return child_read_then_close();
+ } else if (case_id == 10) {
+ return child_max_vms_count();
} else {
return -1;
}