mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-27 12:54:42 +00:00
Work on supporting both epoch and fuel
This commit is contained in:
parent
10670dba70
commit
8974b0c490
3 changed files with 99 additions and 56 deletions
|
@ -23,7 +23,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
async {
|
async {
|
||||||
let mut runtime = PluginBuilder::new_with_default_ctx(PluginYield::default_fuel())
|
let mut runtime = PluginBuilder::new_fuel_with_default_ctx(PluginYield::default_fuel())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.host_function("mystery_number", |input: u32| input + 7)
|
.host_function("mystery_number", |input: u32| input + 7)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -55,27 +55,43 @@ impl<A: Serialize, R: DeserializeOwned> Clone for WasiFn<A, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PluginYield {
|
pub struct PluginYieldEpoch {
|
||||||
Epoch {
|
|
||||||
delta: u64,
|
delta: u64,
|
||||||
epoch: std::time::Duration,
|
epoch: std::time::Duration,
|
||||||
},
|
}
|
||||||
Fuel {
|
|
||||||
|
impl Into<PluginYield> for PluginYieldEpoch {
|
||||||
|
fn into(self) -> PluginYield {
|
||||||
|
PluginYield::Epoch(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PluginYieldFuel {
|
||||||
initial: u64,
|
initial: u64,
|
||||||
refill: u64,
|
refill: u64,
|
||||||
},
|
}
|
||||||
|
|
||||||
|
impl Into<PluginYield> for PluginYieldFuel {
|
||||||
|
fn into(self) -> PluginYield {
|
||||||
|
PluginYield::Fuel(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PluginYield {
|
||||||
|
Epoch(PluginYieldEpoch),
|
||||||
|
Fuel(PluginYieldFuel),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PluginYield {
|
impl PluginYield {
|
||||||
pub fn default_epoch() -> Self {
|
pub fn default_epoch() -> PluginYieldEpoch {
|
||||||
PluginYield::Epoch {
|
PluginYieldEpoch {
|
||||||
delta: 1,
|
delta: 1,
|
||||||
epoch: Duration::from_millis(1),
|
epoch: Duration::from_millis(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_fuel() -> Self {
|
pub fn default_fuel() -> PluginYieldFuel {
|
||||||
PluginYield::Fuel {
|
PluginYieldFuel {
|
||||||
initial: 1000,
|
initial: 1000,
|
||||||
refill: 1000,
|
refill: 1000,
|
||||||
}
|
}
|
||||||
|
@ -91,65 +107,94 @@ pub struct PluginBuilder {
|
||||||
engine: Engine,
|
engine: Engine,
|
||||||
linker: Linker<WasiCtxAlloc>,
|
linker: Linker<WasiCtxAlloc>,
|
||||||
yield_when: PluginYield,
|
yield_when: PluginYield,
|
||||||
created_epoch_incrementer: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PluginBuilder {
|
impl PluginBuilder {
|
||||||
/// Create a new [`PluginBuilder`] with the given WASI context.
|
fn create_engine(yield_when: &PluginYield) -> Result<(Engine, Linker<WasiCtxAlloc>), Error> {
|
||||||
/// Using the default context is a safe bet, see [`new_with_default_context`].
|
|
||||||
pub fn new(wasi_ctx: WasiCtx, yield_when: PluginYield) -> Result<Self, Error> {
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.async_support(true);
|
config.async_support(true);
|
||||||
|
let engine = Engine::new(&config)?;
|
||||||
|
let linker = Linker::new(&engine);
|
||||||
|
|
||||||
match yield_when {
|
match yield_when {
|
||||||
PluginYield::Epoch { .. } => {
|
PluginYield::Epoch(_) => {
|
||||||
config.epoch_interruption(true);
|
config.epoch_interruption(true);
|
||||||
}
|
}
|
||||||
PluginYield::Fuel { .. } => {
|
PluginYield::Fuel(_) => {
|
||||||
config.consume_fuel(true);
|
config.consume_fuel(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let engine = Engine::new(&config)?;
|
Ok((engine, linker))
|
||||||
let linker = Linker::new(&engine);
|
}
|
||||||
|
|
||||||
|
/// Create a new [`PluginBuilder`] with the given WASI context.
|
||||||
|
/// Using the default context is a safe bet, see [`new_with_default_context`].
|
||||||
|
pub fn new_epoch<C>(
|
||||||
|
wasi_ctx: WasiCtx,
|
||||||
|
yield_epoch: PluginYieldEpoch,
|
||||||
|
callback: C,
|
||||||
|
) -> Result<Self, Error>
|
||||||
|
where
|
||||||
|
C: FnOnce(std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'static>>) -> (),
|
||||||
|
{
|
||||||
|
let epoch = yield_epoch.epoch;
|
||||||
|
let yield_when = PluginYield::Epoch(yield_epoch);
|
||||||
|
let (engine, linker) = Self::create_engine(&yield_when)?;
|
||||||
|
|
||||||
|
let engine_ref = &engine;
|
||||||
|
|
||||||
|
// we can't create the future until after initializing
|
||||||
|
// because we need the engine to load the plugin
|
||||||
|
// we could use an Arc, but that'd suck
|
||||||
|
callback(Box::pin(async move {
|
||||||
|
loop {
|
||||||
|
smol::Timer::after(epoch).await;
|
||||||
|
engine_ref.increment_epoch();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
Ok(PluginBuilder {
|
Ok(PluginBuilder {
|
||||||
wasi_ctx,
|
wasi_ctx,
|
||||||
engine,
|
engine,
|
||||||
linker,
|
linker,
|
||||||
yield_when,
|
yield_when,
|
||||||
created_epoch_incrementer: false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `PluginBuilder` that inherits the
|
pub fn new_fuel(wasi_ctx: WasiCtx, yield_fuel: PluginYieldFuel) -> Result<Self, Error> {
|
||||||
|
let yield_when = PluginYield::Fuel(yield_fuel);
|
||||||
|
let (engine, linker) = Self::create_engine(&yield_when)?;
|
||||||
|
|
||||||
|
Ok(PluginBuilder {
|
||||||
|
wasi_ctx,
|
||||||
|
engine,
|
||||||
|
linker,
|
||||||
|
yield_when,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `WasiCtx` that inherits the
|
||||||
/// host processes' access to `stdout` and `stderr`.
|
/// host processes' access to `stdout` and `stderr`.
|
||||||
pub fn new_with_default_ctx(yield_when: PluginYield) -> Result<Self, Error> {
|
fn default_ctx() -> WasiCtx {
|
||||||
let wasi_ctx = WasiCtxBuilder::new()
|
WasiCtxBuilder::new()
|
||||||
.inherit_stdout()
|
.inherit_stdout()
|
||||||
.inherit_stderr()
|
.inherit_stderr()
|
||||||
.build();
|
.build()
|
||||||
Self::new(wasi_ctx, yield_when)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a epoch incrementer if this plugin is configured to increment epochs.
|
pub fn new_epoch_with_default_ctx<C>(
|
||||||
/// Will panic if this plugin is not configured to increment epochs.
|
yield_epoch: PluginYieldEpoch,
|
||||||
pub fn create_epoch_incrementer(
|
callback: C,
|
||||||
&mut self,
|
) -> Result<Self, Error>
|
||||||
) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
where
|
||||||
if let PluginYield::Epoch { epoch, .. } = self.yield_when {
|
C: FnOnce(std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'static>>) -> (),
|
||||||
self.created_epoch_incrementer = true;
|
{
|
||||||
let epoch = epoch.clone();
|
Self::new_epoch(Self::default_ctx(), yield_epoch, callback)
|
||||||
let engine = &self.engine;
|
|
||||||
Box::pin(async move {
|
|
||||||
loop {
|
|
||||||
smol::Timer::after(epoch);
|
|
||||||
engine.increment_epoch();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
panic!("Tried creating an epoch incrementer, but one does not yet exist.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_fuel_with_default_ctx(yield_fuel: PluginYieldFuel) -> Result<Self, Error> {
|
||||||
|
Self::new_fuel(Self::default_ctx(), yield_fuel)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an `async` host function. See [`host_function`] for details.
|
/// Add an `async` host function. See [`host_function`] for details.
|
||||||
|
@ -297,11 +342,6 @@ impl PluginBuilder {
|
||||||
/// Will panic if this is plugin uses `PluginYield::Epoch`,
|
/// Will panic if this is plugin uses `PluginYield::Epoch`,
|
||||||
/// but an epoch incrementer has not yet been created.
|
/// but an epoch incrementer has not yet been created.
|
||||||
pub async fn init<T: AsRef<[u8]>>(self, precompiled: bool, module: T) -> Result<Plugin, Error> {
|
pub async fn init<T: AsRef<[u8]>>(self, precompiled: bool, module: T) -> Result<Plugin, Error> {
|
||||||
if let PluginYield::Epoch { .. } = self.yield_when {
|
|
||||||
if !self.created_epoch_incrementer {
|
|
||||||
panic!("Must create epoch incrementer to run epoch-based plugin");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Plugin::init(precompiled, module.as_ref().to_vec(), self).await
|
Plugin::init(precompiled, module.as_ref().to_vec(), self).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,10 +430,10 @@ impl Plugin {
|
||||||
|
|
||||||
// set up automatic yielding based on configuration
|
// set up automatic yielding based on configuration
|
||||||
match plugin.yield_when {
|
match plugin.yield_when {
|
||||||
PluginYield::Epoch { delta, .. } => {
|
PluginYield::Epoch(PluginYieldEpoch { delta, .. }) => {
|
||||||
store.epoch_deadline_async_yield_and_update(delta);
|
store.epoch_deadline_async_yield_and_update(delta);
|
||||||
}
|
}
|
||||||
PluginYield::Fuel { initial, refill } => {
|
PluginYield::Fuel(PluginYieldFuel { initial, refill }) => {
|
||||||
store.add_fuel(initial).unwrap();
|
store.add_fuel(initial).unwrap();
|
||||||
store.out_of_fuel_async_yield(u64::MAX, refill);
|
store.out_of_fuel_async_yield(u64::MAX, refill);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,10 @@ use std::{any::Any, path::PathBuf, sync::Arc};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
pub async fn new_json(executor: Arc<Background>) -> Result<PluginLspAdapter> {
|
pub async fn new_json(executor: Arc<Background>) -> Result<PluginLspAdapter> {
|
||||||
let plugin = PluginBuilder::new_with_default_ctx(PluginYield::default_epoch())?
|
let plugin =
|
||||||
|
PluginBuilder::new_epoch_with_default_ctx(PluginYield::default_epoch(), |future| {
|
||||||
|
executor.spawn(future).detach()
|
||||||
|
})?
|
||||||
.host_function_async("command", |command: String| async move {
|
.host_function_async("command", |command: String| async move {
|
||||||
let mut args = command.split(' ');
|
let mut args = command.split(' ');
|
||||||
let command = args.next().unwrap();
|
let command = args.next().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue