diff --git a/examples/consumefuel/Program.cs b/examples/consumefuel/Program.cs index b25a22f..73a1805 100644 --- a/examples/consumefuel/Program.cs +++ b/examples/consumefuel/Program.cs @@ -12,7 +12,12 @@ "expensive", Function.FromCallback(store, (Caller caller) => { - var remaining = caller.ConsumeFuel(1000UL); + checked + { + caller.Fuel -= 1000UL; + } + + var remaining = caller.Fuel; Console.WriteLine($"Called an expensive function which consumed 1000 fuel. {remaining} units of fuel remaining."); } )); @@ -26,7 +31,7 @@ return; } -store.AddFuel(5000UL); +store.Fuel += 5000UL; Console.WriteLine("Added 5000 units of fuel"); for (var i = 0; i < 4; i++) diff --git a/src/Caller.cs b/src/Caller.cs index b4f79b2..35cddc1 100644 --- a/src/Caller.cs +++ b/src/Caller.cs @@ -137,33 +137,23 @@ public bool TryGetMemorySpan(string name, long address, int length, out Span< public Store Store => store; /// - /// Adds fuel to this store for WebAssembly code to consume while executing. + /// Gets or sets the fuel available for WebAssembly code to consume while executing. /// - /// The fuel to add to the store. - public void AddFuel(ulong fuel) => context.AddFuel(fuel); - - /// - /// Synthetically consumes fuel from this store. - /// - /// For this method to work fuel consumption must be enabled via . - /// + /// + /// + /// For this property to work, fuel consumption must be enabled via . + /// + /// /// WebAssembly execution will automatically consume fuel but if so desired the embedder can also consume fuel manually /// to account for relative costs of host functions, for example. - /// - /// This method will attempt to consume units of fuel from within this store. If the remaining - /// amount of fuel allows this then the amount of remaining fuel is returned. Otherwise, a - /// is thrown and no fuel is consumed. - /// - /// The fuel to consume from the store. - /// Returns the remaining amount of fuel. - /// Thrown if more fuel is consumed than the store currently has. - public ulong ConsumeFuel(ulong fuel) => context.ConsumeFuel(fuel); - - /// - /// Gets the fuel consumed by the executing WebAssembly code. - /// - /// Returns the fuel consumed by the executing WebAssembly code or 0 if fuel consumption was not enabled. - public ulong GetConsumedFuel() => context.GetConsumedFuel(); + /// + /// + /// The fuel available for WebAssembly code to consume while executing. + public ulong Fuel + { + get => context.GetFuel(); + set => context.SetFuel(value); + } /// /// Gets the user-defined data from the Store. diff --git a/src/Store.cs b/src/Store.cs index 0ee7b8a..6378e6b 100644 --- a/src/Store.cs +++ b/src/Store.cs @@ -20,9 +20,20 @@ internal void GC() Native.wasmtime_context_gc(handle); } - internal void AddFuel(ulong fuel) + internal ulong GetFuel() { - var error = Native.wasmtime_context_add_fuel(handle, fuel); + var error = Native.wasmtime_context_get_fuel(handle, out ulong fuel); + if (error != IntPtr.Zero) + { + throw WasmtimeException.FromOwnedError(error); + } + + return fuel; + } + + internal void SetFuel(ulong fuel) + { + var error = Native.wasmtime_context_set_fuel(handle, fuel); if (error != IntPtr.Zero) { throw WasmtimeException.FromOwnedError(error); @@ -49,27 +60,6 @@ internal Store Store } } - internal ulong ConsumeFuel(ulong fuel) - { - var error = Native.wasmtime_context_consume_fuel(handle, fuel, out var remaining); - if (error != IntPtr.Zero) - { - throw WasmtimeException.FromOwnedError(error); - } - - return remaining; - } - - internal ulong GetConsumedFuel() - { - if (!Native.wasmtime_context_fuel_consumed(handle, out var fuel)) - { - return 0; - } - - return fuel; - } - internal void SetWasiConfiguration(WasiConfiguration config) { var wasi = config.Build(); @@ -97,14 +87,10 @@ private static class Native public static extern void wasmtime_context_gc(IntPtr handle); [DllImport(Engine.LibraryName)] - public static extern IntPtr wasmtime_context_add_fuel(IntPtr handle, ulong fuel); + public static extern IntPtr wasmtime_context_set_fuel(IntPtr handle, ulong fuel); [DllImport(Engine.LibraryName)] - public static extern IntPtr wasmtime_context_consume_fuel(IntPtr handle, ulong fuel, out ulong remaining); - - [DllImport(Engine.LibraryName)] - [return: MarshalAs(UnmanagedType.I1)] - public static extern bool wasmtime_context_fuel_consumed(IntPtr handle, out ulong fuel); + public static extern IntPtr wasmtime_context_get_fuel(IntPtr handle, out ulong fuel); [DllImport(Engine.LibraryName)] public static extern IntPtr wasmtime_context_set_wasi(IntPtr handle, IntPtr config); @@ -158,6 +144,35 @@ public Store(Engine engine, object? data) handle = new Handle(Native.wasmtime_store_new(engine.NativeHandle, (IntPtr)storeHandle, Finalizer)); } + /// + /// Gets or sets the fuel available for WebAssembly code to consume while executing. + /// + /// + /// + /// For this property to work fuel consumption must be enabled via . + /// + /// + /// WebAssembly execution will automatically consume fuel but if so desired the embedder can also consume fuel manually + /// to account for relative costs of host functions, for example. + /// + /// + /// The fuel available for WebAssembly code to consume while executing. + public ulong Fuel + { + get + { + ulong fuel = Context.GetFuel(); + System.GC.KeepAlive(this); + return fuel; + } + + set + { + Context.SetFuel(value); + System.GC.KeepAlive(this); + } + } + /// /// Limit the resources that this store may consume. Note that the limits are only used to limit the creation/growth of resources in the future, /// this does not retroactively attempt to apply limits to the store. @@ -212,49 +227,6 @@ public void GC() System.GC.KeepAlive(this); } - /// - /// Adds fuel to this store for WebAssembly code to consume while executing. - /// - /// The fuel to add to the store. - public void AddFuel(ulong fuel) - { - Context.AddFuel(fuel); - System.GC.KeepAlive(this); - } - - /// - /// Synthetically consumes fuel from this store. - /// - /// For this method to work fuel consumption must be enabled via . - /// - /// WebAssembly execution will automatically consume fuel but if so desired the embedder can also consume fuel manually - /// to account for relative costs of host functions, for example. - /// - /// This method will attempt to consume units of fuel from within this store. If the remaining - /// amount of fuel allows this then the amount of remaining fuel is returned. Otherwise, a - /// is thrown and no fuel is consumed. - /// - /// The fuel to consume from the store. - /// Returns the remaining amount of fuel. - /// Thrown if more fuel is consumed than the store currently has. - public ulong ConsumeFuel(ulong fuel) - { - var result = Context.ConsumeFuel(fuel); - System.GC.KeepAlive(this); - return result; - } - - /// - /// Gets the fuel consumed by the executing WebAssembly code. - /// - /// Returns the fuel consumed by the executing WebAssembly code or 0 if fuel consumption was not enabled. - public ulong GetConsumedFuel() - { - var result = Context.GetConsumedFuel(); - System.GC.KeepAlive(this); - return result; - } - /// /// Configures WASI within the store. /// diff --git a/tests/CallerTests.cs b/tests/CallerTests.cs index e866ce2..9daf308 100644 --- a/tests/CallerTests.cs +++ b/tests/CallerTests.cs @@ -30,7 +30,7 @@ public CallerTests(CallerFixture fixture) Linker = new Linker(Fixture.Engine); Store = new Store(Fixture.Engine); - Store.AddFuel(1000000); + Store.Fuel = 1000000; } [Fact] @@ -309,11 +309,12 @@ public void ItCanSetData() [Fact] - public void ItCanConsumeFuel() + public void ItCanRemoveFuel() { Linker.DefineFunction("env", "callback", (Caller c) => { - c.ConsumeFuel(10).Should().Be(1000000 - (10 + 2)); + c.Fuel -= 10; + c.Fuel.Should().Be(1000000 - (10 + 2)); }); var instance = Linker.Instantiate(Store, Fixture.Module); @@ -323,7 +324,7 @@ public void ItCanConsumeFuel() // 10 is consumed by the explicit fuel consumption // 2 is consumed by the rest of the WASM which executes behind the scenes in this test - Store.GetConsumedFuel().Should().Be(10 + 2); + Store.Fuel.Should().Be(1000000 - (10 + 2)); } [Fact] @@ -331,8 +332,8 @@ public void ItCanAddFuel() { Linker.DefineFunction("env", "callback", (Caller c) => { - c.AddFuel(2); - c.ConsumeFuel(0).Should().Be(1000000); + c.Fuel += 2; + c.Fuel.Should().Be(1000000); }); var instance = Linker.Instantiate(Store, Fixture.Module); @@ -341,15 +342,15 @@ public void ItCanAddFuel() callback.Invoke(); // 2 is consumed by the WASM which executes behind the scenes in this test - Store.GetConsumedFuel().Should().Be(2); + Store.Fuel.Should().Be(1000000); } [Fact] - public void ItCanGetConsumedFuel() + public void ItCanGetFuel() { Linker.DefineFunction("env", "callback", (Caller c) => { - c.GetConsumedFuel().Should().Be(2); + c.Fuel.Should().Be(1000000 - 2); }); var instance = Linker.Instantiate(Store, Fixture.Module); diff --git a/tests/FuelConsumptionTests.cs b/tests/FuelConsumptionTests.cs index e7c29bb..267f57f 100644 --- a/tests/FuelConsumptionTests.cs +++ b/tests/FuelConsumptionTests.cs @@ -35,61 +35,41 @@ public FuelConsumptionTests(FuelConsumptionFixture fixture) })); Linker.Define("env", "expensive", Function.FromCallback(Store, (Caller caller) => { - caller.ConsumeFuel(100UL); + checked + { + caller.Fuel -= 100UL; + } })); } [Fact] - public void ItConsumesNoFuelBeforeFuelIsAdded() + public void ItHasNoFuelBeforeFuelIsAdded() { - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(0UL); + Store.Fuel.Should().Be(0UL); } [Fact] - public void ItConsumesNoFuelWhenFuelIsAdded() + public void ItCanSetAndGetFuel() { - Store.AddFuel(1000UL); - - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(0UL); - } - - [Fact] - public void ItConsumesAddedFuel() - { - Store.AddFuel(1000UL); - var remaining = Store.ConsumeFuel(250UL); - remaining.Should().Be(750UL); - - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(250UL); + Store.Fuel = 1000UL; + Store.Fuel.Should().Be(1000UL); } [Fact] - public void ItCanConsumeZeroFuel() + public void ItCanAddFuel() { - Store.AddFuel(1000UL); - var remaining = Store.ConsumeFuel(0UL); - remaining.Should().Be(1000UL); + Store.Fuel = 1000UL; + Store.Fuel += 1000UL; - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(0UL); + Store.Fuel.Should().Be(2000UL); } - [Fact] - public void ItThrowsOnConsumingTooMuchFuel() + public void ItCanRemoveFuel() { - Store.AddFuel(1000UL); - - Action action = () => Store.ConsumeFuel(2000UL); - action - .Should() - .Throw() - .WithMessage("not enough fuel remaining in store*"); + Store.Fuel = 1000UL; + Store.Fuel -= 500UL; - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(0UL); + Store.Fuel.Should().Be(500UL); } [Fact] @@ -98,15 +78,13 @@ public void ItConsumesFuelWhenCallingImportMethods() var instance = Linker.Instantiate(Store, Fixture.Module); var free = instance.GetFunction("free").WrapFunc(); - Store.AddFuel(1000UL); + Store.Fuel = 1000UL; free.Invoke().Type.Should().Be(ResultType.Ok); - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(2UL); + Store.Fuel.Should().Be(1000UL - 2UL); free.Invoke().Type.Should().Be(ResultType.Ok); - consumed = Store.GetConsumedFuel(); - consumed.Should().Be(4UL); + Store.Fuel.Should().Be(1000UL - 4UL); } [Fact] @@ -115,11 +93,10 @@ public void ItConsumesFuelFromInsideAnImportMethod() var instance = Linker.Instantiate(Store, Fixture.Module); var expensive = instance.GetFunction("expensive"); - Store.AddFuel(1000UL); + Store.Fuel = 1000UL; expensive.Invoke(); - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(102UL); + Store.Fuel.Should().Be(1000UL - 102UL); } [Fact] @@ -135,8 +112,7 @@ public void ItThrowsOnCallingImportMethodIfNoFuelAdded() .Where(e => e.Type == TrapCode.OutOfFuel) .WithMessage("*all fuel consumed by WebAssembly*"); - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(1UL); + Store.Fuel.Should().Be(0UL); } [Fact] @@ -145,15 +121,13 @@ public void ItThrowsOnCallingImportMethodIfNotEnoughFuelAdded() var instance = Linker.Instantiate(Store, Fixture.Module); var free = instance.GetFunction("free"); - Store.AddFuel(4UL); + Store.Fuel = 4UL; free.Invoke(); - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(2UL); + Store.Fuel.Should().Be(2UL); free.Invoke(); - consumed = Store.GetConsumedFuel(); - consumed.Should().Be(4UL); + Store.Fuel.Should().Be(0UL); Action action = () => free.Invoke(); action @@ -162,8 +136,7 @@ public void ItThrowsOnCallingImportMethodIfNotEnoughFuelAdded() .Where(e => e.Type == TrapCode.OutOfFuel) .WithMessage("*all fuel consumed by WebAssembly*"); - consumed = Store.GetConsumedFuel(); - consumed.Should().Be(5UL); + Store.Fuel.Should().Be(0UL); } [Fact] @@ -172,16 +145,15 @@ public void ItThrowsWhenConsumingTooMuchFuelFromInsideAnImportMethod() var instance = Linker.Instantiate(Store, Fixture.Module); var expensive = instance.GetFunction("expensive"); - Store.AddFuel(50UL); + Store.Fuel = 50UL; Action action = () => expensive.Invoke(); action .Should() .Throw() - .WithMessage("*not enough fuel remaining in store*"); + .WithInnerException(); - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(2UL); + Store.Fuel.Should().Be(48UL); } [Fact] @@ -190,15 +162,13 @@ public void ItAddsAdditonalFuelAfterCallingImportMethods() var instance = Linker.Instantiate(Store, Fixture.Module); var free = instance.GetFunction("free"); - Store.AddFuel(4UL); + Store.Fuel = 4UL; free.Invoke(); - var consumed = Store.GetConsumedFuel(); - consumed.Should().Be(2UL); + Store.Fuel.Should().Be(2UL); free.Invoke(); - consumed = Store.GetConsumedFuel(); - consumed.Should().Be(4UL); + Store.Fuel.Should().Be(0UL); Action action = () => free.Invoke(); action @@ -207,14 +177,10 @@ public void ItAddsAdditonalFuelAfterCallingImportMethods() .Where(e => e.Type == TrapCode.OutOfFuel) .WithMessage("*all fuel consumed by WebAssembly*"); - consumed = Store.GetConsumedFuel(); - consumed.Should().Be(5UL); - - Store.AddFuel(3UL); + Store.Fuel += 3UL; free.Invoke(); - consumed = Store.GetConsumedFuel(); - consumed.Should().Be(7UL); + Store.Fuel.Should().Be(1UL); action .Should() @@ -222,8 +188,7 @@ public void ItAddsAdditonalFuelAfterCallingImportMethods() .Where(e => e.Type == TrapCode.OutOfFuel) .WithMessage("*all fuel consumed by WebAssembly*"); - consumed = Store.GetConsumedFuel(); - consumed.Should().Be(8UL); + Store.Fuel.Should().Be(0UL); } public void Dispose()