Skip to content

Commit 972ef24

Browse files
Yesehpeterhuene
andauthored
Implement bindings for StoreContext data (#191)
* Implement binding for wasmtime_context_get_data * Remove data setter native method * Dispose data with Store * Revert "Dispose data with Store" This reverts commit 09fdf03. * Add data finalizer to stroe constructor * Only add finalizer if data is defined * Use IntPtr instead of UnmanagedType.IUnknown * Remove GetData<T>() from caller * Remove test for generic GetData * Remove unneeded test * Add test for undefined data * Fix test * Fix XML doc * Add binding for wasmtime_context_set_data * Add storedata example * Add working testcases * Ensure finalizer is called when passing no data, test GC for store data * Update SetData signatures to be explicitly nullable * Prevent undefined behaviour in SetData * Apply suggestions from code review Co-authored-by: Peter Huene <[email protected]> Co-authored-by: Peter Huene <[email protected]>
1 parent a859f17 commit 972ef24

File tree

7 files changed

+361
-4
lines changed

7 files changed

+361
-4
lines changed

Examples.sln

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "table", "examples\table\tab
1717
EndProject
1818
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasmtime", "src\Wasmtime.csproj", "{82B01E4A-1CAD-44BB-B923-11FA37173BD7}"
1919
EndProject
20-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "consumefuel", "examples\consumefuel\consumefuel.csproj", "{F79FAA27-3CA6-4CC3-BB50-CE27191770F1}"
20+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "consumefuel", "examples\consumefuel\consumefuel.csproj", "{F79FAA27-3CA6-4CC3-BB50-CE27191770F1}"
21+
EndProject
22+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "storedata", "storedata\storedata.csproj", "{E8749BAF-9D8B-4CF9-ACFF-490E86D52A20}"
2123
EndProject
2224
Global
2325
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -57,6 +59,10 @@ Global
5759
{F79FAA27-3CA6-4CC3-BB50-CE27191770F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
5860
{F79FAA27-3CA6-4CC3-BB50-CE27191770F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
5961
{F79FAA27-3CA6-4CC3-BB50-CE27191770F1}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{E8749BAF-9D8B-4CF9-ACFF-490E86D52A20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63+
{E8749BAF-9D8B-4CF9-ACFF-490E86D52A20}.Debug|Any CPU.Build.0 = Debug|Any CPU
64+
{E8749BAF-9D8B-4CF9-ACFF-490E86D52A20}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{E8749BAF-9D8B-4CF9-ACFF-490E86D52A20}.Release|Any CPU.Build.0 = Release|Any CPU
6066
EndGlobalSection
6167
GlobalSection(SolutionProperties) = preSolution
6268
HideSolutionNode = FALSE

src/Caller.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,17 @@ private IntPtr NativeHandle
127127
/// <returns>Returns the fuel consumed by the executing WebAssembly code or 0 if fuel consumption was not enabled.</returns>
128128
public ulong GetConsumedFuel() => ((IStore)this).Context.GetConsumedFuel();
129129

130+
/// <summary>
131+
/// Gets the user-defined data from the Store's Context.
132+
/// </summary>
133+
/// <returns>An object represeting the user defined data from this Store</returns>
134+
public object? GetData() => ((IStore)this).Context.GetData();
135+
136+
/// <summary>
137+
/// Replaces the user-defined data in the Store's Context
138+
/// </summary>
139+
public void SetData(object? data) => ((IStore)this).Context.SetData(data);
140+
130141
internal static class Native
131142
{
132143
[DllImport(Engine.LibraryName)]

src/Store.cs

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,35 @@ internal void AddFuel(ulong fuel)
2828
}
2929
}
3030

31+
internal object? GetData()
32+
{
33+
var data = Native.wasmtime_context_get_data(handle);
34+
if (data == IntPtr.Zero)
35+
{
36+
return null;
37+
}
38+
39+
return GCHandle.FromIntPtr(data).Target;
40+
}
41+
42+
internal void SetData(object? data)
43+
{
44+
var oldData = Native.wasmtime_context_get_data(handle);
45+
46+
var newPtr = IntPtr.Zero;
47+
if (data != null)
48+
{
49+
newPtr = (IntPtr)GCHandle.Alloc(data);
50+
}
51+
52+
Native.wasmtime_context_set_data(handle, newPtr);
53+
54+
if (oldData != IntPtr.Zero)
55+
{
56+
GCHandle.FromIntPtr(oldData).Free();
57+
}
58+
}
59+
3160
internal ulong ConsumeFuel(ulong fuel)
3261
{
3362
var error = Native.wasmtime_context_consume_fuel(handle, fuel, out var remaining);
@@ -90,6 +119,12 @@ private static class Native
90119

91120
[DllImport(Engine.LibraryName)]
92121
public static extern void wasmtime_context_set_epoch_deadline(IntPtr handle, ulong ticksBeyondCurrent);
122+
123+
[DllImport(Engine.LibraryName)]
124+
public static extern IntPtr wasmtime_context_get_data(IntPtr handle);
125+
126+
[DllImport(Engine.LibraryName)]
127+
public static extern void wasmtime_context_set_data(IntPtr handle, IntPtr data);
93128
}
94129

95130
internal readonly IntPtr handle;
@@ -119,14 +154,25 @@ public class Store : IStore, IDisposable
119154
/// Constructs a new store.
120155
/// </summary>
121156
/// <param name="engine">The engine to use for the store.</param>
122-
public Store(Engine engine)
157+
public Store(Engine engine) : this(engine, null) { }
158+
159+
/// <summary>
160+
/// Constructs a new store with the given context data.
161+
/// </summary>
162+
/// <param name="engine">The engine to use for the store.</param>
163+
/// <param name="data">The data to initialize the store with; this can later be accessed with the GetData function.</param>
164+
public Store(Engine engine, object? data)
123165
{
124166
if (engine is null)
125167
{
126168
throw new ArgumentNullException(nameof(engine));
127169
}
128170

129-
handle = new Handle(Native.wasmtime_store_new(engine.NativeHandle, IntPtr.Zero, null));
171+
var dataPtr = data != null
172+
? (IntPtr)GCHandle.Alloc(data)
173+
: IntPtr.Zero;
174+
175+
handle = new Handle(Native.wasmtime_store_new(engine.NativeHandle, dataPtr, Finalizer));
130176
}
131177

132178
/// <summary>
@@ -164,7 +210,7 @@ public Store(Engine engine)
164210
public ulong GetConsumedFuel() => ((IStore)this).Context.GetConsumedFuel();
165211

166212
/// <summary>
167-
/// Configres WASI within the store.
213+
/// Configures WASI within the store.
168214
/// </summary>
169215
/// <param name="config">The WASI configuration to use.</param>
170216
public void SetWasiConfiguration(WasiConfiguration config) => ((IStore)this).Context.SetWasiConfiguration(config);
@@ -175,6 +221,19 @@ public Store(Engine engine)
175221
/// <param name="ticksBeyondCurrent"></param>
176222
public void SetEpochDeadline(ulong ticksBeyondCurrent) => ((IStore)this).Context.SetEpochDeadline(ticksBeyondCurrent);
177223

224+
/// <summary>
225+
/// Retrieves the data stored in the Store context
226+
/// </summary>
227+
public object? GetData() => ((IStore)this).Context.GetData();
228+
229+
/// <summary>
230+
/// Replaces the data stored in the Store context
231+
/// </summary>
232+
public void SetData(object? data)
233+
{
234+
((IStore)this).Context.SetData(data);
235+
}
236+
178237
StoreContext IStore.Context => new StoreContext(Native.wasmtime_store_context(NativeHandle));
179238

180239
/// <inheritdoc/>
@@ -226,5 +285,13 @@ private static class Native
226285
}
227286

228287
private readonly Handle handle;
288+
289+
private static readonly Native.Finalizer Finalizer = (p) =>
290+
{
291+
if (p != IntPtr.Zero)
292+
{
293+
GCHandle.FromIntPtr(p).Free();
294+
}
295+
};
229296
}
230297
}

storedata/Program.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using Wasmtime;
3+
4+
namespace Example
5+
{
6+
class Program
7+
{
8+
private class StoreData
9+
{
10+
public int Id { get; set; }
11+
12+
public string Message { get; set; }
13+
14+
public StoreData(string message, int id = 0)
15+
{
16+
Message = message;
17+
Id = id;
18+
}
19+
20+
public override string ToString()
21+
{
22+
return $"ID {Id}: {Message}";
23+
}
24+
}
25+
26+
static void Main(string[] args)
27+
{
28+
using var engine = new Engine(new Config().WithReferenceTypes(true));
29+
using var module = Module.FromTextFile(engine, "storedata.wat");
30+
using var linker = new Linker(engine);
31+
32+
var storeData = new StoreData("Hello, WASM", 0);
33+
using var store = new Store(engine, storeData);
34+
35+
linker.DefineFunction("", "store_data", (Caller caller) =>
36+
{
37+
var data = caller.GetData() as StoreData;
38+
39+
Console.WriteLine($"ID {data.Id}: {data.Message}"); // 'ID 0: Hello, WASM'
40+
41+
// Fully replace store data
42+
var newStoreData = new StoreData("Store data replaced", 1);
43+
caller.SetData(newStoreData);
44+
data = caller.GetData() as StoreData;
45+
46+
Console.WriteLine($"ID {data.Id}: {data.Message}"); // 'ID 1: Store data replaced'
47+
48+
// Change properties normally
49+
data.Message = "Properties changed";
50+
data.Id = 2;
51+
});
52+
53+
var instance = linker.Instantiate(store, module);
54+
55+
var run = instance.GetAction("run");
56+
if (run is null)
57+
{
58+
Console.WriteLine("error: run export is missing");
59+
return;
60+
}
61+
run();
62+
63+
// Retrieve final data from store directly
64+
var data = store.GetData() as StoreData;
65+
Console.WriteLine($"ID {data.Id}: {data.Message}"); // 'ID 2: Properties changed'
66+
}
67+
}
68+
}

storedata/storedata.csproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\src\Wasmtime.csproj" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<None Update="storedata.wat">
16+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
17+
</None>
18+
</ItemGroup>
19+
20+
</Project>

storedata/storedata.wat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(module
2+
(type $t0 (func))
3+
(import "" "store_data" (func $.store_data (type $t0)))
4+
(func $run
5+
call $.store_data
6+
)
7+
(export "run" (func $run))
8+
)

0 commit comments

Comments
 (0)