mirror of
https://github.com/extism/extism.git
synced 2026-01-11 23:08:06 -05:00
Compare commits
4 Commits
readme-ver
...
kernel-imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3300fed506 | ||
|
|
b026d30865 | ||
|
|
e8d0397754 | ||
|
|
39685a9038 |
@@ -13,7 +13,7 @@ description = "Traits to make Rust types usable with Extism"
|
||||
anyhow = "1.0.75"
|
||||
base64 = "~0.22"
|
||||
bytemuck = {version = "1.14.0", optional = true }
|
||||
prost = { version = "0.14.1", optional = true }
|
||||
prost = { version = "0.13.1", optional = true }
|
||||
protobuf = { version = "3.2.0", optional = true }
|
||||
rmp-serde = { version = "1.1.2", optional = true }
|
||||
serde = "1.0.186"
|
||||
|
||||
@@ -263,24 +263,24 @@ impl MemoryRoot {
|
||||
let mem_left = self_length - self_position - core::mem::size_of::<MemoryRoot>() as u64;
|
||||
let length_with_block = length + core::mem::size_of::<MemoryBlock>() as u64;
|
||||
|
||||
// If the current position is large enough to hold the length of the block being
|
||||
// allocated then check for existing free blocks that can be re-used before
|
||||
// growing memory
|
||||
if length_with_block <= self_position {
|
||||
let b = self.find_free_block(length, self_position);
|
||||
|
||||
// If there's a free block then re-use it
|
||||
if let Some(b) = b {
|
||||
b.used = length as usize;
|
||||
b.status
|
||||
.store(MemoryStatus::Active as u8, Ordering::Release);
|
||||
return Some(b);
|
||||
}
|
||||
}
|
||||
|
||||
// When the allocation is larger than the number of bytes available
|
||||
// we will need to try to grow the memory
|
||||
if length_with_block >= mem_left {
|
||||
// If the current position is large enough to hold the length of the block being
|
||||
// allocated then check for existing free blocks that can be re-used before
|
||||
// growing memory
|
||||
if length_with_block <= self_position {
|
||||
let b = self.find_free_block(length, self_position);
|
||||
|
||||
// If there's a free block then re-use it
|
||||
if let Some(b) = b {
|
||||
b.used = length as usize;
|
||||
b.status
|
||||
.store(MemoryStatus::Active as u8, Ordering::Release);
|
||||
return Some(b);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the number of pages needed to cover the remaining bytes
|
||||
let npages = num_pages(length_with_block - mem_left);
|
||||
let x = core::arch::wasm32::memory_grow(0, npages);
|
||||
|
||||
@@ -9,46 +9,29 @@ repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
wasmtime = { version = ">= 27.0.0, < 31.0.0", default-features = false, features = [
|
||||
'cache',
|
||||
'gc',
|
||||
'gc-drc',
|
||||
'cranelift',
|
||||
'coredump',
|
||||
'wat',
|
||||
'parallel-compilation',
|
||||
'pooling-allocator',
|
||||
'demangle',
|
||||
] }
|
||||
wasi-common = { version = ">= 27.0.0, < 31.0.0" }
|
||||
wiggle = { version = ">= 27.0.0, < 31.0.0" }
|
||||
wasmtime = {version = ">= 27.0.0, < 31.0.0"}
|
||||
wasi-common = {version = ">= 27.0.0, < 31.0.0"}
|
||||
wiggle = {version = ">= 27.0.0, < 31.0.0"}
|
||||
anyhow = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
toml = "0.9"
|
||||
toml = "0.8"
|
||||
sha2 = "0.10"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3.18", features = [
|
||||
"std",
|
||||
"env-filter",
|
||||
"fmt",
|
||||
] }
|
||||
tracing-subscriber = {version = "0.3.18", features = ["std", "env-filter", "fmt"]}
|
||||
url = "2"
|
||||
glob = "0.3"
|
||||
ureq = { version = "3.0", optional = true }
|
||||
ureq = {version = "3.0", optional=true}
|
||||
extism-manifest = { workspace = true }
|
||||
extism-convert = { workspace = true, features = ["extism-path"] }
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
libc = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["http", "register-http", "register-filesystem", "wasmtime-default-features"]
|
||||
register-http = ["ureq"] # enables wasm to be downloaded using http
|
||||
register-filesystem = [] # enables wasm to be loaded from disk
|
||||
http = ["ureq"] # enables extism_http_request
|
||||
wasmtime-default-features = [
|
||||
'wasmtime/default',
|
||||
]
|
||||
default = ["http", "register-http", "register-filesystem"]
|
||||
register-http = ["ureq"] # enables wasm to be downloaded using http
|
||||
register-filesystem = [] # enables wasm to be loaded from disk
|
||||
http = ["ureq"] # enables extism_http_request
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = { version = "0.29", default-features = false }
|
||||
|
||||
@@ -12,7 +12,7 @@ To use the `extism` crate, you can add it to your Cargo file:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
extism = "*"
|
||||
extism = "1.4.1"
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
@@ -112,8 +112,7 @@ let mut plugin = Plugin::new(&manifest, [], true);
|
||||
let res = plugin.call::<&str, &str>("count_vowels", "Yellow, world!").unwrap();
|
||||
println!("{}", res);
|
||||
# => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
|
||||
let manifest = Manifest::new([url]).with_config_key("vowels", "aeiouyAEIOUY");
|
||||
let mut plugin = Plugin::new(&manifest, [], true).unwrap();
|
||||
let mut plugin = Plugin::new(&manifest, [], true).with_config_key("vowels", "aeiouyAEIOUY");
|
||||
let res = plugin.call::<&str, &str>("count_vowels", "Yellow, world!").unwrap();
|
||||
println!("{}", res);
|
||||
# => {"count": 4, "total": 4, "vowels": "aeiouyAEIOUY"}
|
||||
|
||||
Binary file not shown.
@@ -258,16 +258,12 @@ pub(crate) fn http_request(
|
||||
};
|
||||
let buf: &[u8] = data.memory_bytes(handle)?;
|
||||
let agent = ureq::agent();
|
||||
let config = agent
|
||||
.configure_request(r.body(buf)?)
|
||||
.http_status_as_error(false);
|
||||
let config = agent.configure_request(r.body(buf)?);
|
||||
let req = config.timeout_global(timeout).build();
|
||||
ureq::run(req)
|
||||
} else {
|
||||
let agent = ureq::agent();
|
||||
let config = agent
|
||||
.configure_request(r.body(())?)
|
||||
.http_status_as_error(false);
|
||||
let config = agent.configure_request(r.body(())?);
|
||||
let req = config.timeout_global(timeout).build();
|
||||
ureq::run(req)
|
||||
};
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{Error, FromBytesOwned, Plugin, ToBytes};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
// `PoolBuilder` is used to configure and create `Pool`s
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -82,8 +79,7 @@ unsafe impl Sync for PoolInner {}
|
||||
#[derive(Clone)]
|
||||
pub struct Pool {
|
||||
config: PoolBuilder,
|
||||
inner: Arc<std::sync::Mutex<PoolInner>>,
|
||||
existing_functions: Arc<RwLock<HashMap<String, bool>>>,
|
||||
inner: std::sync::Arc<std::sync::Mutex<PoolInner>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Pool {}
|
||||
@@ -92,7 +88,13 @@ unsafe impl Sync for Pool {}
|
||||
impl Pool {
|
||||
/// Create a new pool with the default configuration
|
||||
pub fn new<F: 'static + Fn() -> Result<Plugin, Error>>(source: F) -> Self {
|
||||
Self::new_from_builder(Box::new(source), PoolBuilder::default())
|
||||
Pool {
|
||||
config: Default::default(),
|
||||
inner: std::sync::Arc::new(std::sync::Mutex::new(PoolInner {
|
||||
plugin_source: Box::new(source),
|
||||
instances: Default::default(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new pool configured using a `PoolBuilder`
|
||||
@@ -102,11 +104,10 @@ impl Pool {
|
||||
) -> Self {
|
||||
Pool {
|
||||
config: builder,
|
||||
inner: Arc::new(std::sync::Mutex::new(PoolInner {
|
||||
inner: std::sync::Arc::new(std::sync::Mutex::new(PoolInner {
|
||||
plugin_source: Box::new(source),
|
||||
instances: Default::default(),
|
||||
})),
|
||||
existing_functions: RwLock::new(HashMap::default()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,26 +171,4 @@ impl Pool {
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Returns `true` if the given function exists, otherwise `false`. Results are cached
|
||||
/// after the first call.
|
||||
pub fn function_exists(&self, name: &str, timeout: std::time::Duration) -> Result<bool, Error> {
|
||||
// read current value if any
|
||||
let read = self.existing_functions.read().unwrap();
|
||||
let exists_opt = read.get(name).cloned();
|
||||
drop(read);
|
||||
if let Some(exists) = exists_opt {
|
||||
Ok(exists)
|
||||
} else {
|
||||
// load plugin and call function_exists
|
||||
let plugin = self.get(timeout)?;
|
||||
let exists = plugin.unwrap().0.borrow().function_exists(name);
|
||||
|
||||
// write result to hashmap
|
||||
let mut write = self.existing_functions.write().unwrap();
|
||||
write.insert(name.to_string(), exists);
|
||||
|
||||
Ok(exists)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,26 @@ fn test_kernel_allocations() {
|
||||
extism_free(&mut store, instance, q);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kernel_page_allocations() {
|
||||
let (mut store, mut instance) = init_kernel_test();
|
||||
let instance = &mut instance;
|
||||
|
||||
let a = extism_alloc(&mut store, instance, 65500 * 4);
|
||||
let a_size = extism_length(&mut store, instance, a);
|
||||
let b = extism_alloc(&mut store, instance, 65539);
|
||||
|
||||
extism_free(&mut store, instance, a);
|
||||
let c = extism_alloc(&mut store, instance, 65500 * 2);
|
||||
let c_size = extism_length(&mut store, instance, c);
|
||||
|
||||
let d = extism_alloc(&mut store, instance, 65500);
|
||||
|
||||
assert_eq!(a + (a_size - c_size), c);
|
||||
assert!(c < b);
|
||||
assert!(d < b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kernel_error() {
|
||||
let (mut store, mut instance) = init_kernel_test();
|
||||
@@ -436,3 +456,31 @@ quickcheck! {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn check_block_reuse(allocs: Vec<u16>) -> bool {
|
||||
let (mut store, mut instance) = init_kernel_test();
|
||||
let instance = &mut instance;
|
||||
let init = extism_alloc(&mut store, instance, allocs.iter().map(|x| *x as u64).sum::<u64>() + allocs.len() as u64 * 64);
|
||||
let bounds = init + extism_length(&mut store, instance, init);
|
||||
extism_free(&mut store, instance, init);
|
||||
for a in allocs {
|
||||
let ptr = extism_alloc(&mut store, instance, a as u64);
|
||||
if ptr == 0 {
|
||||
continue
|
||||
}
|
||||
if extism_length(&mut store, instance, ptr) != a as u64 {
|
||||
return false
|
||||
}
|
||||
|
||||
extism_free(&mut store, instance , ptr);
|
||||
|
||||
if ptr > bounds {
|
||||
println!("ptr={ptr}, bounds={bounds}");
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::*;
|
||||
use std::time::Duration;
|
||||
|
||||
fn run_thread(p: Pool, i: u64) -> std::thread::JoinHandle<()> {
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(Duration::from_millis(i));
|
||||
std::thread::sleep(std::time::Duration::from_millis(i));
|
||||
let s: String = p
|
||||
.get(Duration::from_secs(1))
|
||||
.get(std::time::Duration::from_secs(1))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.call("count_vowels", "abc")
|
||||
@@ -14,20 +13,17 @@ fn run_thread(p: Pool, i: u64) -> std::thread::JoinHandle<()> {
|
||||
})
|
||||
}
|
||||
|
||||
fn init(max_instances: usize) -> Pool {
|
||||
let data = include_bytes!("../../../wasm/code.wasm");
|
||||
let plugin_builder =
|
||||
extism::PluginBuilder::new(extism::Manifest::new([extism::Wasm::data(data)]))
|
||||
.with_wasi(true);
|
||||
PoolBuilder::new()
|
||||
.with_max_instances(max_instances)
|
||||
.build(move || plugin_builder.clone().build())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_threads() {
|
||||
for i in 1..=3 {
|
||||
let pool = init(i);
|
||||
let data = include_bytes!("../../../wasm/code.wasm");
|
||||
let plugin_builder =
|
||||
extism::PluginBuilder::new(extism::Manifest::new([extism::Wasm::data(data)]))
|
||||
.with_wasi(true);
|
||||
let pool: Pool = PoolBuilder::new()
|
||||
.with_max_instances(i)
|
||||
.build(move || plugin_builder.clone().build());
|
||||
|
||||
let threads = vec![
|
||||
run_thread(pool.clone(), 1000),
|
||||
run_thread(pool.clone(), 1000),
|
||||
@@ -50,14 +46,3 @@ fn test_threads() {
|
||||
assert!(pool.count() <= i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exists() -> Result<(), Error> {
|
||||
let pool = init(1);
|
||||
let timeout = Duration::from_secs(1);
|
||||
assert!(pool.function_exists("count_vowels", timeout)?);
|
||||
assert!(pool.function_exists("count_vowels", timeout)?);
|
||||
assert!(!pool.function_exists("not_existing", timeout)?);
|
||||
assert!(!pool.function_exists("not_existing", timeout)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user