mirror of
https://github.com/extism/extism.git
synced 2026-01-11 23:08:06 -05:00
Compare commits
10 Commits
custom-htt
...
extend-tim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bafdfb710 | ||
|
|
e1d33800f0 | ||
|
|
6084b69790 | ||
|
|
5896729cfb | ||
|
|
0f93c5ef9d | ||
|
|
ab812d9281 | ||
|
|
2087398513 | ||
|
|
212e28bec3 | ||
|
|
fd95729d8d | ||
|
|
49e28892bc |
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -28,48 +28,56 @@ jobs:
|
||||
target: 'x86_64-apple-darwin'
|
||||
artifact: 'libextism.dylib'
|
||||
static-artifact: 'libextism.a'
|
||||
static-dll-artifact: ''
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'macos'
|
||||
target: 'aarch64-apple-darwin'
|
||||
artifact: 'libextism.dylib'
|
||||
static-artifact: 'libextism.a'
|
||||
static-dll-artifact: ''
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'ubuntu'
|
||||
target: 'aarch64-unknown-linux-gnu'
|
||||
artifact: 'libextism.so'
|
||||
static-artifact: 'libextism.a'
|
||||
static-dll-artifact: ''
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'ubuntu'
|
||||
target: 'aarch64-unknown-linux-musl'
|
||||
artifact: 'libextism.so'
|
||||
static-artifact: 'libextism.a'
|
||||
static-dll-artifact: ''
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'ubuntu'
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
artifact: 'libextism.so'
|
||||
static-artifact: 'libextism.a'
|
||||
static-dll-artifact: ''
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'ubuntu'
|
||||
target: 'x86_64-unknown-linux-musl'
|
||||
artifact: ''
|
||||
static-artifact: 'libextism.a'
|
||||
static-dll-artifact: ''
|
||||
pc-in: ''
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'windows'
|
||||
target: 'x86_64-pc-windows-gnu'
|
||||
artifact: 'extism.dll'
|
||||
static-artifact: 'libextism.a'
|
||||
static-dll-artifact: 'libextism.dll.a'
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'windows'
|
||||
target: 'x86_64-pc-windows-msvc'
|
||||
artifact: 'extism.dll'
|
||||
static-artifact: 'extism.lib'
|
||||
static-dll-artifact: 'extism.dll.lib'
|
||||
pc-in: ''
|
||||
static-pc-in: ''
|
||||
|
||||
@@ -158,7 +166,8 @@ jobs:
|
||||
cp LICENSE ${SRC_DIR}
|
||||
tar -C ${SRC_DIR} -czvf ${ARCHIVE} extism.h \
|
||||
${{ matrix.artifact }} ${{ matrix.static-artifact }} \
|
||||
${{ matrix.pc-in }} ${{ matrix.static-pc-in }}
|
||||
${{ matrix.pc-in }} ${{ matrix.static-pc-in }} \
|
||||
${{ matrix.static-dll-artifact }}
|
||||
ls -ll ${ARCHIVE}
|
||||
|
||||
if &>/dev/null which shasum; then
|
||||
|
||||
@@ -306,9 +306,26 @@ impl MemoryRoot {
|
||||
if !Self::pointer_in_bounds_fast(offs) {
|
||||
return None;
|
||||
}
|
||||
let ptr = offs - core::mem::size_of::<MemoryBlock>() as u64;
|
||||
let ptr = ptr as *mut MemoryBlock;
|
||||
Some(&mut *ptr)
|
||||
|
||||
// Get the first block
|
||||
let mut block = self.blocks.as_mut_ptr();
|
||||
|
||||
// Only loop while the block pointer is less then the current position
|
||||
while (block as u64) < self.blocks.as_ptr() as u64 + offs {
|
||||
let b = &mut *block;
|
||||
|
||||
// Get the block status, this lets us know if we are able to re-use it
|
||||
let status = b.status.load(Ordering::Acquire);
|
||||
|
||||
if status == MemoryStatus::Active as u8 && b.data.as_ptr() as Pointer == offs {
|
||||
return Some(b);
|
||||
}
|
||||
|
||||
// Get the next block
|
||||
block = b.next_ptr();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -231,20 +231,15 @@ pub struct Manifest {
|
||||
pub allowed_paths: Option<BTreeMap<PathBuf, PathBuf>>,
|
||||
|
||||
/// The plugin timeout, by default this is set to 30s
|
||||
#[serde(default = "default_timeout")]
|
||||
#[serde(default)]
|
||||
pub timeout_ms: Option<u64>,
|
||||
}
|
||||
|
||||
fn default_timeout() -> Option<u64> {
|
||||
Some(30000)
|
||||
}
|
||||
|
||||
impl Manifest {
|
||||
/// Create a new manifest
|
||||
pub fn new(wasm: impl IntoIterator<Item = impl Into<Wasm>>) -> Manifest {
|
||||
Manifest {
|
||||
wasm: wasm.into_iter().map(|x| x.into()).collect(),
|
||||
timeout_ms: default_timeout(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,11 @@ typedef void (*ExtismFunctionType)(ExtismCurrentPlugin *plugin,
|
||||
ExtismSize n_outputs,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Log drain callback
|
||||
*/
|
||||
typedef void (*ExtismLogDrainFunctionType)(const char *data, ExtismSize size);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -132,12 +137,6 @@ ExtismSize extism_current_plugin_memory_length(ExtismCurrentPlugin *plugin, Exti
|
||||
*/
|
||||
void extism_current_plugin_memory_free(ExtismCurrentPlugin *plugin, ExtismMemoryHandle ptr);
|
||||
|
||||
/**
|
||||
* Add milliseconds to a plug-in's timeout
|
||||
* NOTE: this should only be called from host functions.
|
||||
*/
|
||||
bool extism_current_plugin_timeout_add_ms(ExtismCurrentPlugin *plugin, uint64_t ms);
|
||||
|
||||
/**
|
||||
* Create a new host function
|
||||
*
|
||||
@@ -174,6 +173,13 @@ void extism_function_free(ExtismFunction *f);
|
||||
*/
|
||||
void extism_function_set_namespace(ExtismFunction *ptr, const char *namespace_);
|
||||
|
||||
/**
|
||||
* Set the cost of an `ExtismFunction`, when set to 0 this has no effect, when set to `1` this will add
|
||||
* the runtime of the function back to the plugin timer.
|
||||
*/
|
||||
void extism_function_set_cost(ExtismFunction *ptr,
|
||||
double cost);
|
||||
|
||||
/**
|
||||
* Create a new plugin with host functions, the functions passed to this function no longer need to be manually freed using
|
||||
*
|
||||
@@ -270,7 +276,7 @@ bool extism_log_custom(const char *log_level);
|
||||
* Calls the provided callback function for each buffered log line.
|
||||
* This is only needed when `extism_log_custom` is used.
|
||||
*/
|
||||
void extism_log_drain(void (*handler)(const char*, uintptr_t));
|
||||
void extism_log_drain(ExtismLogDrainFunctionType handler);
|
||||
|
||||
/**
|
||||
* Reset the Extism runtime, this will invalidate all allocated memory
|
||||
|
||||
@@ -421,12 +421,6 @@ impl CurrentPlugin {
|
||||
let length = self.memory_length(offs).unwrap_or_default();
|
||||
(offs, length)
|
||||
}
|
||||
|
||||
/// Create a new `TimeoutManager` that can be used to adjust timeouts for the
|
||||
/// current plugin. Returns `None` when no timeout has been configured.
|
||||
pub fn timeout_manager(&self) -> Option<TimeoutManager> {
|
||||
TimeoutManager::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Internal for CurrentPlugin {
|
||||
|
||||
Binary file not shown.
@@ -155,7 +155,7 @@ unsafe impl<T> Sync for UserData<T> {}
|
||||
unsafe impl Send for CPtr {}
|
||||
unsafe impl Sync for CPtr {}
|
||||
|
||||
type FunctionInner = dyn Fn(wasmtime::Caller<CurrentPlugin>, &[wasmtime::Val], &mut [wasmtime::Val]) -> Result<(), Error>
|
||||
pub(crate) type FunctionInner = dyn Fn(wasmtime::Caller<CurrentPlugin>, &[wasmtime::Val], &mut [wasmtime::Val]) -> Result<(), Error>
|
||||
+ Sync
|
||||
+ Send;
|
||||
|
||||
@@ -174,6 +174,9 @@ pub struct Function {
|
||||
/// Function handle
|
||||
pub(crate) f: Arc<FunctionInner>,
|
||||
|
||||
/// Function cost (in terms of time)
|
||||
pub(crate) cost: f64,
|
||||
|
||||
/// UserData
|
||||
pub(crate) _user_data: UserDataHandle,
|
||||
}
|
||||
@@ -204,10 +207,12 @@ impl Function {
|
||||
ty,
|
||||
f: Arc::new(
|
||||
move |mut caller: Caller<_>, inp: &[Val], outp: &mut [Val]| {
|
||||
let plugin = caller.data_mut();
|
||||
let x = data.clone();
|
||||
f(caller.data_mut(), inp, outp, x)
|
||||
f(plugin, inp, outp, x)
|
||||
},
|
||||
),
|
||||
cost: 0.0,
|
||||
namespace: None,
|
||||
_user_data: match &user_data {
|
||||
UserData::C(ptr) => UserDataHandle::C(ptr.clone()),
|
||||
@@ -239,6 +244,23 @@ impl Function {
|
||||
self
|
||||
}
|
||||
|
||||
/// Function cost
|
||||
pub fn cost(&self) -> f64 {
|
||||
self.cost
|
||||
}
|
||||
|
||||
/// Set host function cost
|
||||
pub fn set_cost(&mut self, cost: f64) {
|
||||
trace!("Setting cost for {} to {cost}", self.name);
|
||||
self.cost = cost;
|
||||
}
|
||||
|
||||
/// Update host function cost
|
||||
pub fn with_cost(mut self, cost: f64) -> Self {
|
||||
self.set_cost(cost);
|
||||
self
|
||||
}
|
||||
|
||||
/// Get function type
|
||||
pub fn ty(&self) -> &wasmtime::FuncType {
|
||||
&self.ty
|
||||
|
||||
@@ -26,9 +26,9 @@ pub use extism_manifest::{Manifest, Wasm, WasmMetadata};
|
||||
pub use function::{Function, UserData, Val, ValType, PTR};
|
||||
pub use plugin::{CancelHandle, Plugin, WasmInput, EXTISM_ENV_MODULE, EXTISM_USER_MODULE};
|
||||
pub use plugin_builder::{DebugOptions, PluginBuilder};
|
||||
pub use timeout_manager::TimeoutManager;
|
||||
|
||||
pub(crate) use internal::{Internal, Wasi};
|
||||
pub(crate) use timeout_manager::TimeoutManager;
|
||||
pub(crate) use timer::{Timer, TimerAction};
|
||||
pub(crate) use tracing::{debug, error, trace, warn};
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// All the functions in the file are exposed from inside WASM plugins
|
||||
use crate::*;
|
||||
|
||||
@@ -142,26 +140,6 @@ pub(crate) fn var_set(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct Resolver;
|
||||
|
||||
impl ureq::Resolver for Resolver {
|
||||
fn resolve(&self, netloc: &str) -> std::io::Result<Vec<std::net::SocketAddr>> {
|
||||
let addrs = std::net::ToSocketAddrs::to_socket_addrs(netloc)?.into_iter();
|
||||
let mut addrs: Vec<_> = addrs.collect();
|
||||
addrs.sort_by(|a, b| {
|
||||
if a.is_ipv4() && b.is_ipv6() {
|
||||
Ordering::Less
|
||||
} else if a.is_ipv6() && b.is_ipv4() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
});
|
||||
Ok(addrs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Make an HTTP request
|
||||
/// Params: i64 (offset to JSON encoded HttpRequest), i64 (offset to body or 0)
|
||||
/// Returns: i64 (offset)
|
||||
@@ -172,7 +150,6 @@ pub(crate) fn http_request(
|
||||
) -> Result<(), Error> {
|
||||
let data: &mut CurrentPlugin = caller.data_mut();
|
||||
let http_req_offset = args!(input, 0, i64) as u64;
|
||||
|
||||
#[cfg(not(feature = "http"))]
|
||||
{
|
||||
let handle = match data.memory_handle(http_req_offset) {
|
||||
@@ -224,8 +201,7 @@ pub(crate) fn http_request(
|
||||
)));
|
||||
}
|
||||
|
||||
let agent = ureq::builder().resolver(Resolver).build();
|
||||
let mut r = agent.request(req.method.as_deref().unwrap_or("GET"), &req.url);
|
||||
let mut r = ureq::request(req.method.as_deref().unwrap_or("GET"), &req.url);
|
||||
|
||||
for (k, v) in req.headers.iter() {
|
||||
r = r.set(k, v);
|
||||
|
||||
@@ -298,9 +298,13 @@ impl Plugin {
|
||||
for f in &mut imports {
|
||||
let name = f.name().to_string();
|
||||
let ns = f.namespace().unwrap_or(EXTISM_USER_MODULE);
|
||||
unsafe {
|
||||
linker.func_new(ns, &name, f.ty().clone(), &*(f.f.as_ref() as *const _))?;
|
||||
}
|
||||
let cost = f.cost();
|
||||
let inner: &function::FunctionInner = unsafe { &*(f.f.as_ref() as *const _) };
|
||||
linker.func_new(ns, &name, f.ty().clone(), move |caller, params, results| {
|
||||
let _timeout =
|
||||
TimeoutManager::new(caller.data()).map(|x| x.with_cost(cost.clone()));
|
||||
inner(caller, params, results)
|
||||
})?;
|
||||
}
|
||||
|
||||
let instance_pre = linker.instantiate_pre(main)?;
|
||||
|
||||
@@ -38,6 +38,9 @@ pub type ExtismFunctionType = extern "C" fn(
|
||||
data: *mut std::ffi::c_void,
|
||||
);
|
||||
|
||||
/// Log drain callback
|
||||
pub type ExtismLogDrainFunctionType = extern "C" fn(data: *const std::ffi::c_char, size: Size);
|
||||
|
||||
impl From<&wasmtime::Val> for ExtismVal {
|
||||
fn from(value: &wasmtime::Val) -> Self {
|
||||
match value.ty() {
|
||||
@@ -143,25 +146,6 @@ pub unsafe extern "C" fn extism_current_plugin_memory_free(
|
||||
}
|
||||
}
|
||||
|
||||
/// Add milliseconds to a plug-in's timeout
|
||||
/// NOTE: this should only be called from host functions.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn extism_current_plugin_timeout_add_ms(
|
||||
plugin: *mut CurrentPlugin,
|
||||
ms: u64,
|
||||
) -> bool {
|
||||
if plugin.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let plugin = &mut *plugin;
|
||||
if let Some(mgr) = plugin.timeout_manager() {
|
||||
return mgr.add(std::time::Duration::from_millis(ms)).is_ok();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Create a new host function
|
||||
///
|
||||
/// Arguments
|
||||
@@ -274,6 +258,18 @@ pub unsafe extern "C" fn extism_function_set_namespace(
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the cost of an `ExtismFunction`, when set to 0 this has no effect, when set to `1` this will add
|
||||
/// the runtime of the function back to the plugin timer.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn extism_function_set_cost(ptr: *mut ExtismFunction, cost: f64) {
|
||||
let f = &mut *ptr;
|
||||
if let Some(x) = f.0.get_mut() {
|
||||
x.set_cost(cost);
|
||||
} else {
|
||||
debug!("Trying to set the cost of already registered function")
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new plugin with host functions, the functions passed to this function no longer need to be manually freed using
|
||||
///
|
||||
/// `wasm`: is a WASM module (wat or wasm) or a JSON encoded manifest
|
||||
@@ -686,11 +682,11 @@ unsafe fn set_log_buffer(filter: &str) -> Result<(), Error> {
|
||||
#[no_mangle]
|
||||
/// Calls the provided callback function for each buffered log line.
|
||||
/// This is only needed when `extism_log_custom` is used.
|
||||
pub unsafe extern "C" fn extism_log_drain(handler: extern "C" fn(*const std::ffi::c_char, usize)) {
|
||||
pub unsafe extern "C" fn extism_log_drain(handler: ExtismLogDrainFunctionType) {
|
||||
if let Some(buf) = &mut LOG_BUFFER {
|
||||
if let Ok(mut buf) = buf.buffer.lock() {
|
||||
for (line, len) in buf.drain(..) {
|
||||
handler(line.as_ptr(), len);
|
||||
handler(line.as_ptr(), len as u64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,10 @@ fn test_kernel_allocations() {
|
||||
// 512 bytes, test block re-use + splitting
|
||||
let p = extism_alloc(&mut store, instance, 512);
|
||||
assert_eq!(extism_length(&mut store, instance, p), 512);
|
||||
assert_eq!(extism_length(&mut store, instance, p + 1), 0);
|
||||
assert_eq!(extism_length(&mut store, instance, p + 2), 0);
|
||||
assert_eq!(extism_length(&mut store, instance, p + 3), 0);
|
||||
assert_eq!(extism_length(&mut store, instance, p + 4), 0);
|
||||
extism_free(&mut store, instance, p);
|
||||
|
||||
// 128 bytes, should be split off the 512 byte block
|
||||
@@ -210,7 +214,7 @@ fn test_kernel_allocations() {
|
||||
let r = extism_alloc(&mut store, instance, 128);
|
||||
assert!(p <= r && r < p + 512);
|
||||
assert!(r > p);
|
||||
assert_eq!(extism_length(&mut store, instance, q), 128);
|
||||
assert_eq!(extism_length(&mut store, instance, r), 128);
|
||||
extism_free(&mut store, instance, q);
|
||||
|
||||
// 100 pages
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
use crate::*;
|
||||
|
||||
enum DropBehavior {
|
||||
Add,
|
||||
Sub,
|
||||
}
|
||||
|
||||
/// `TimeoutManager` is used to control `Plugin` timeouts from within host functions
|
||||
///
|
||||
/// It can be used to add or subtract time from a plug-in's timeout. If a plugin is not
|
||||
/// configured to have a timeout then this will have no effect.
|
||||
pub struct TimeoutManager {
|
||||
pub(crate) struct TimeoutManager {
|
||||
start_time: std::time::Instant,
|
||||
id: uuid::Uuid,
|
||||
tx: std::sync::mpsc::Sender<TimerAction>,
|
||||
cost: f64,
|
||||
drop_behavior: Option<DropBehavior>,
|
||||
}
|
||||
|
||||
impl TimeoutManager {
|
||||
@@ -25,48 +19,27 @@ impl TimeoutManager {
|
||||
start_time: std::time::Instant::now(),
|
||||
id: plugin.id.clone(),
|
||||
tx: Timer::tx(),
|
||||
cost: 1.0,
|
||||
drop_behavior: None,
|
||||
cost: 0.0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add to the configured timeout
|
||||
pub fn add(&self, duration: std::time::Duration) -> Result<(), Error> {
|
||||
let d = duration.mul_f64(self.cost);
|
||||
/// Add the amount of time this value has existed to the configured timeout
|
||||
pub fn add_elapsed(&mut self) -> Result<(), Error> {
|
||||
let cost = self.cost.abs();
|
||||
let d = self.start_time.elapsed().mul_f64(cost);
|
||||
let mut d: timer::ExtendTimeout = d.into();
|
||||
if self.cost.is_sign_negative() {
|
||||
d = -d;
|
||||
}
|
||||
self.tx.send(TimerAction::Extend {
|
||||
id: self.id.clone(),
|
||||
duration: d.into(),
|
||||
duration: d,
|
||||
})?;
|
||||
self.start_time = std::time::Instant::now();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Subtract from the configured timeout
|
||||
pub fn sub(&self, duration: std::time::Duration) -> Result<(), Error> {
|
||||
let d: timer::ExtendTimeout = duration.mul_f64(self.cost).into();
|
||||
self.tx.send(TimerAction::Extend {
|
||||
id: self.id.clone(),
|
||||
duration: -d,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// When the `TimeoutManager` is dropped the elapsed duration since it was created will be
|
||||
/// added to the plug-in's timeout
|
||||
pub fn add_on_drop(mut self) -> Self {
|
||||
self.drop_behavior = Some(DropBehavior::Add);
|
||||
self
|
||||
}
|
||||
|
||||
/// When the `TimeoutManager` is dropped the elapsed duration since it was created will be
|
||||
/// added to the plug-in's timeout
|
||||
pub fn sub_on_drop(mut self) -> Self {
|
||||
self.drop_behavior = Some(DropBehavior::Sub);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adjust the cost of added/subtracted values, this will scale all durations
|
||||
/// submitted to this manager by the provided factor.
|
||||
pub fn cost(mut self, cost: f64) -> Self {
|
||||
pub fn with_cost(mut self, cost: f64) -> Self {
|
||||
self.cost = cost;
|
||||
self
|
||||
}
|
||||
@@ -74,19 +47,13 @@ impl TimeoutManager {
|
||||
|
||||
impl Drop for TimeoutManager {
|
||||
fn drop(&mut self) {
|
||||
if let Some(b) = &self.drop_behavior {
|
||||
let duration = self.start_time.elapsed();
|
||||
let x = match b {
|
||||
DropBehavior::Add => self.add(duration),
|
||||
DropBehavior::Sub => self.sub(duration),
|
||||
};
|
||||
if let Err(e) = x {
|
||||
error!(
|
||||
plugin = self.id.to_string(),
|
||||
"unable to extend timeout: {}",
|
||||
e.to_string()
|
||||
);
|
||||
}
|
||||
let x = self.add_elapsed();
|
||||
if let Err(e) = x {
|
||||
error!(
|
||||
plugin = self.id.to_string(),
|
||||
"unable to extend timeout: {}",
|
||||
e.to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user