#![allow(
clippy::string_lit_as_bytes,
clippy::manual_string_new,
clippy::needless_pass_by_value,
clippy::uninlined_format_args,
clippy::needless_collect,
clippy::cast_sign_loss
)]
use std::collections::BTreeMap;
use std::sync::Arc;
use std::thread;
use wacc::{
storage::{Pairs, Stack},
types::{CheckCount, ContextPath},
vm::{Builder, Context, ModuleCache, Value},
};
use wasmtime::StoreLimitsBuilder;
const MEMORY_LIMIT: usize = 1 << 22;
const SIMPLE_WASM: &[u8] = &[
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x0a, 0x06, 0x01, 0x04, 0x00, 0x41, 0x01, 0x0b, ];
#[derive(Default, Clone)]
struct Kvp {
pub pairs: BTreeMap<String, Value>,
}
impl Pairs for Kvp {
fn get(&self, key: &str) -> Option<Value> {
self.pairs.get(key).cloned()
}
fn put(&mut self, key: &str, value: &Value) -> Option<Value> {
self.pairs.insert(key.to_string(), value.clone())
}
}
#[derive(Default, Clone)]
struct Stk {
pub stack: Vec<Value>,
}
impl Stack for Stk {
fn push(&mut self, value: Value) {
self.stack.push(value);
}
fn pop(&mut self) -> Option<Value> {
self.stack.pop()
}
fn top(&self) -> Option<Value> {
self.stack.last().cloned()
}
fn peek(&self, idx: usize) -> Option<Value> {
if idx >= self.stack.len() {
return None;
}
Some(self.stack[self.stack.len() - 1 - idx].clone())
}
fn len(&self) -> usize {
self.stack.len()
}
fn is_empty(&self) -> bool {
self.stack.is_empty()
}
}
#[test]
fn test_parallel_instance_creation() {
let handles: Vec<_> = (0..4)
.map(|_| {
thread::spawn(|| {
let kvp = Kvp::default();
let pstack = Stk::default();
let rstack = Stk::default();
let context = Context {
current: Box::new(kvp.clone()),
proposed: Box::new(kvp),
pstack: Box::new(pstack),
rstack: Box::new(rstack),
check_count: CheckCount::zero(),
write_idx: 0,
context: ContextPath::root(),
log: Vec::default(),
limiter: StoreLimitsBuilder::new().memory_size(MEMORY_LIMIT).build(),
};
let mut instance = Builder::new()
.with_context(context)
.with_bytes(SIMPLE_WASM)
.try_build()
.unwrap();
instance.run("test").unwrap()
})
})
.collect();
let results: Vec<bool> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert!(
results.iter().all(|&r| r),
"All parallel executions should succeed"
);
assert_eq!(results.len(), 4);
}
#[test]
fn test_module_cache_concurrent_access() {
let cache = Arc::new(ModuleCache::new());
let key = ModuleCache::hash_bytes(SIMPLE_WASM);
let handles: Vec<_> = (0..8)
.map(|i| {
let cache = Arc::clone(&cache);
thread::spawn(move || {
if cache.get(key).is_some() {
return true;
}
if i == 0 {
}
cache.get(key).is_some()
})
})
.collect();
let _results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
}
#[test]
fn test_value_is_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<Value>();
assert_sync::<Value>();
}
#[test]
fn test_cache_is_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<ModuleCache>();
assert_sync::<ModuleCache>();
}
#[test]
fn test_concurrent_cache_operations() {
let cache = Arc::new(ModuleCache::with_capacity(10));
let handles: Vec<_> = (0..4)
.map(|i| {
let cache = Arc::clone(&cache);
thread::spawn(move || {
let key = (i as u64) * 1000;
let _ = cache.get(key);
cache.len()
})
})
.collect();
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(results.len(), 4);
}
#[test]
fn test_cache_basic_functionality() {
let cache = ModuleCache::new();
assert_eq!(cache.len(), 0);
assert!(cache.is_empty());
let key = 12345u64;
assert!(cache.get(key).is_none());
cache.clear();
assert_eq!(cache.len(), 0);
}
#[test]
fn test_parallel_value_sharing() {
let value: Value = vec![1, 2, 3, 4, 5].into();
let value = Arc::new(value);
let handles: Vec<_> = (0..4)
.map(|_| {
let value = Arc::clone(&value);
thread::spawn(move || {
match &*value {
Value::Bin { data, .. } => data.len(),
_ => 0,
}
})
})
.collect();
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert!(results.iter().all(|&len| len == 5));
}