mirror of
https://github.com/extism/extism.git
synced 2026-01-11 23:08:06 -05:00
Compare commits
1 Commits
main
...
fix-clippy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bebf1cb0c |
@@ -9,54 +9,35 @@ repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
wasmtime = { version="37", default-features = false, features = [
|
||||
'cache',
|
||||
'gc',
|
||||
'gc-drc',
|
||||
'cranelift',
|
||||
'coredump',
|
||||
'wat',
|
||||
'parallel-compilation',
|
||||
'pooling-allocator',
|
||||
'demangle',
|
||||
] }
|
||||
wasi-common = "37"
|
||||
wiggle = "37"
|
||||
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-exceptions = [] # enables exception-handling proposal in wasmtime (requires wasmtime gc feature)
|
||||
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 }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.7.0"
|
||||
criterion = "0.6.0"
|
||||
quickcheck = "1"
|
||||
rand = "0.9.0"
|
||||
|
||||
|
||||
@@ -191,17 +191,6 @@ ExtismCompiledPlugin *extism_compiled_plugin_new(const uint8_t *wasm,
|
||||
bool with_wasi,
|
||||
char **errmsg);
|
||||
|
||||
/**
|
||||
* Pre-compile an Extism plugin and set the number of instructions a plugin is allowed to execute
|
||||
*/
|
||||
ExtismCompiledPlugin *extism_compiled_plugin_new_with_fuel_limit(const uint8_t *wasm,
|
||||
ExtismSize wasm_size,
|
||||
const ExtismFunction **functions,
|
||||
ExtismSize n_functions,
|
||||
bool with_wasi,
|
||||
uint64_t fuel_limit,
|
||||
char **errmsg);
|
||||
|
||||
/**
|
||||
* Free `ExtismCompiledPlugin`
|
||||
*/
|
||||
|
||||
@@ -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,6 @@
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
path::PathBuf,
|
||||
sync::TryLockError,
|
||||
};
|
||||
|
||||
@@ -61,16 +60,26 @@ impl CompiledPlugin {
|
||||
.wasm_tail_call(true)
|
||||
.wasm_function_references(true)
|
||||
.wasm_gc(true);
|
||||
#[cfg(feature = "wasmtime-exceptions")]
|
||||
{
|
||||
config.wasm_exceptions(true);
|
||||
}
|
||||
|
||||
if builder.options.fuel.is_some() {
|
||||
config.consume_fuel(true);
|
||||
}
|
||||
|
||||
config.cache(Self::configure_cache(&builder.options.cache_config)?);
|
||||
match &builder.options.cache_config {
|
||||
Some(None) => (),
|
||||
Some(Some(path)) => {
|
||||
config.cache_config_load(path)?;
|
||||
}
|
||||
None => {
|
||||
if let Ok(env) = std::env::var("EXTISM_CACHE_CONFIG") {
|
||||
if !env.is_empty() {
|
||||
config.cache_config_load(&env)?;
|
||||
}
|
||||
} else {
|
||||
config.cache_config_load_default()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let engine = Engine::new(&config)?;
|
||||
|
||||
@@ -88,43 +97,6 @@ impl CompiledPlugin {
|
||||
engine,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return optional cache according to builder options.
|
||||
fn configure_cache(
|
||||
cache_opt: &Option<Option<std::path::PathBuf>>,
|
||||
) -> Result<Option<wasmtime::Cache>, Error> {
|
||||
match cache_opt {
|
||||
// Explicitly disabled
|
||||
Some(None) => Ok(None),
|
||||
|
||||
// Explicit path
|
||||
Some(Some(p)) => {
|
||||
let cache = wasmtime::Cache::from_file(Some(p.as_path()))?;
|
||||
Ok(Some(cache))
|
||||
}
|
||||
|
||||
// Unspecified, try environment, then system fallback
|
||||
None => {
|
||||
match std::env::var_os("EXTISM_CACHE_CONFIG") {
|
||||
Some(val) => {
|
||||
if val.is_empty() {
|
||||
// Disable cache if env var exists but is empty
|
||||
Ok(None)
|
||||
} else {
|
||||
let p = PathBuf::from(val);
|
||||
let cache = wasmtime::Cache::from_file(Some(p.as_path()))?;
|
||||
Ok(Some(cache))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// load cache configuration from the system default path
|
||||
let cache = wasmtime::Cache::from_file(None)?;
|
||||
Ok(Some(cache))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Plugin contains everything needed to execute a WASM function
|
||||
@@ -372,7 +344,6 @@ fn relink(
|
||||
let (kind, ty) = match import.ty() {
|
||||
ExternType::Func(t) => ("function", t.to_string()),
|
||||
ExternType::Global(t) => ("global", t.content().to_string()),
|
||||
ExternType::Tag(t) => ("tag", t.ty().to_string()),
|
||||
ExternType::Table(t) => ("table", t.element().to_string()),
|
||||
ExternType::Memory(_) => ("memory", String::new()),
|
||||
};
|
||||
|
||||
@@ -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)]
|
||||
@@ -49,7 +46,7 @@ impl PoolPlugin {
|
||||
}
|
||||
|
||||
/// Access the underlying plugin
|
||||
pub fn plugin(&self) -> std::cell::RefMut<'_, Plugin> {
|
||||
pub fn plugin(&self) -> std::cell::RefMut<Plugin> {
|
||||
self.0.borrow_mut()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,70 +363,6 @@ pub unsafe extern "C" fn extism_compiled_plugin_new(
|
||||
})
|
||||
}
|
||||
|
||||
/// Pre-compile an Extism plugin and set the number of instructions a plugin is allowed to execute
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn extism_compiled_plugin_new_with_fuel_limit(
|
||||
wasm: *const u8,
|
||||
wasm_size: Size,
|
||||
functions: *mut *const ExtismFunction,
|
||||
n_functions: Size,
|
||||
with_wasi: bool,
|
||||
fuel_limit: u64,
|
||||
errmsg: *mut *mut std::ffi::c_char,
|
||||
) -> *mut CompiledPlugin {
|
||||
trace!("Call to extism_plugin_new with wasm pointer {:?}", wasm);
|
||||
let data = std::slice::from_raw_parts(wasm, wasm_size as usize);
|
||||
|
||||
let mut builder = PluginBuilder::new(data)
|
||||
.with_wasi(with_wasi)
|
||||
.with_fuel_limit(fuel_limit);
|
||||
|
||||
if !functions.is_null() {
|
||||
let funcs = (0..n_functions)
|
||||
.map(|i| unsafe { *functions.add(i as usize) })
|
||||
.map(|ptr| {
|
||||
if ptr.is_null() {
|
||||
return Err("Cannot pass null pointer");
|
||||
}
|
||||
|
||||
let ExtismFunction(func) = &*ptr;
|
||||
let Some(func) = func.take() else {
|
||||
return Err("Function cannot be registered with multiple different Plugins");
|
||||
};
|
||||
|
||||
Ok(func)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap_or_else(|e| {
|
||||
if !errmsg.is_null() {
|
||||
let e = std::ffi::CString::new(e.to_string()).unwrap();
|
||||
*errmsg = e.into_raw();
|
||||
}
|
||||
Vec::new()
|
||||
});
|
||||
|
||||
if funcs.len() != n_functions as usize {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
builder = builder.with_functions(funcs);
|
||||
}
|
||||
|
||||
CompiledPlugin::new(builder)
|
||||
.map(|v| Box::into_raw(Box::new(v)))
|
||||
.unwrap_or_else(|e| {
|
||||
if !errmsg.is_null() {
|
||||
let e = std::ffi::CString::new(format!(
|
||||
"Unable to compile Extism plugin: {}",
|
||||
e.root_cause(),
|
||||
))
|
||||
.unwrap();
|
||||
*errmsg = e.into_raw();
|
||||
}
|
||||
std::ptr::null_mut()
|
||||
})
|
||||
}
|
||||
|
||||
/// Free `ExtismCompiledPlugin`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn extism_compiled_plugin_free(plugin: *mut CompiledPlugin) {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -133,7 +133,9 @@ fn it_works() {
|
||||
.unwrap();
|
||||
let native_avg: std::time::Duration = native_sum / native_num_tests as u32;
|
||||
|
||||
println!("native function call (avg, N = {native_num_tests}): {native_avg:?}");
|
||||
println!(
|
||||
"native function call (avg, N = {native_num_tests}): {native_avg:?}"
|
||||
);
|
||||
|
||||
let num_tests = test_times.len();
|
||||
let sum: std::time::Duration = test_times
|
||||
|
||||
Reference in New Issue
Block a user