Skip to content

Commit 560a13b

Browse files
authored
Update to recent Wasmtime C API changes regarding values (#318)
* Update to recent Wasmtime C API changes regarding values. This includes updates for: - bytecodealliance/wasmtime#8451 - bytecodealliance/wasmtime#8461 - bytecodealliance/wasmtime#8011 TODOs: - Allocating an `externref` can now fail (by `wasmtime_externref_new` returning `false`). Currently, we throw a `WasmtimeException` in that case. We need to check where that exception can be thrown, and whether we need to do any additional clean-up (e.g. when converting arguments for a function call). - Check whether it's ok to compare the `__private` field of externs (which has been remaned in the C API, previously it was `index`). - `anyref` type is not yet supported, but I'm not sure what exactly it is and whether we need to add it. Fixes #315 * Follow-Up: Make fields private. * Ensure to clean-up `Value` instances (in arguments for a function call, and in results for an untyped callback) when e.g. allocating an `externref` fails. We don't need to do such a clean-up for unchecked function calls that use `ValueRaw` because in that case we don't own `externref` values. * Avoid accessing the `__private` fields in tests by checking the whole struct for equality (which is the case when all members are equal). * Use separate dictionaries for caching `Function`, `Memory`, and `Global` objects in the `Store`, which avoids having to explicitly accessing the `__private` field (because the whole struct is now compared). Additionally, it is more type-safe (since we don't need to cast the `object`).
1 parent b2b0480 commit 560a13b

File tree

8 files changed

+316
-114
lines changed

8 files changed

+316
-114
lines changed

src/Externs.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,35 @@ namespace Wasmtime
77
internal struct ExternFunc
88
{
99
public ulong store;
10-
public UIntPtr index;
10+
private nuint __private;
1111
}
1212

1313
[StructLayout(LayoutKind.Sequential)]
1414
internal struct ExternTable
1515
{
1616
public ulong store;
17-
public UIntPtr index;
17+
private nuint __private;
1818
}
1919

2020
[StructLayout(LayoutKind.Sequential)]
2121
internal struct ExternMemory
2222
{
2323
public ulong store;
24-
public UIntPtr index;
24+
private nuint __private;
2525
}
2626

2727
[StructLayout(LayoutKind.Sequential)]
2828
internal struct ExternInstance
2929
{
3030
public ulong store;
31-
public UIntPtr index;
31+
private nuint __private;
3232
}
3333

3434
[StructLayout(LayoutKind.Sequential)]
3535
internal struct ExternGlobal
3636
{
3737
public ulong store;
38-
public UIntPtr index;
38+
private nuint __private;
3939
}
4040

4141
internal enum ExternKind : byte
@@ -44,6 +44,7 @@ internal enum ExternKind : byte
4444
Global,
4545
Table,
4646
Memory,
47+
SharedMemory,
4748
}
4849

4950
[StructLayout(LayoutKind.Explicit)]
@@ -60,6 +61,9 @@ internal struct ExternUnion
6061

6162
[FieldOffset(0)]
6263
public ExternMemory memory;
64+
65+
[FieldOffset(0)]
66+
public IntPtr sharedmemory;
6367
}
6468

6569
[StructLayout(LayoutKind.Sequential)]

src/Function.cs

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ out externFunc
8888
/// <summary>
8989
/// Determines if the underlying function reference is null.
9090
/// </summary>
91-
public bool IsNull => func.index == UIntPtr.Zero && func.store == 0;
91+
public bool IsNull => func.IsNull();
9292

9393
/// <summary>
9494
/// Represents a null function reference.
@@ -295,14 +295,28 @@ private unsafe void InvokeWithoutReturn(Span<ValueRaw> arguments, StoreContext s
295295
Span<Value> args = stackalloc Value[Parameters.Count];
296296
for (var i = 0; i < arguments.Length; ++i)
297297
{
298-
args[i] = arguments[i].ToValue(store, Parameters[i]);
299-
}
298+
try
299+
{
300+
args[i] = arguments[i].ToValue(store, Parameters[i]);
301+
}
302+
catch
303+
{
304+
// Clean-up the previous values in case an exception occured (e.g. when
305+
// `wasmtime_externref_new` failed).
306+
for (int releaseIndex = 0; releaseIndex < i; releaseIndex++)
307+
{
308+
args[releaseIndex].Release(store);
309+
}
300310

301-
// Make some space to store the return results
302-
Span<Value> resultsSpan = stackalloc Value[Results.Count];
311+
throw;
312+
}
313+
}
303314

304315
try
305316
{
317+
// Make some space to store the return results
318+
Span<Value> resultsSpan = stackalloc Value[Results.Count];
319+
306320
var trap = Invoke(args, resultsSpan);
307321
if (trap != IntPtr.Zero)
308322
{
@@ -333,18 +347,17 @@ private unsafe void InvokeWithoutReturn(Span<ValueRaw> arguments, StoreContext s
333347
{
334348
for (int i = 0; i < Results.Count; ++i)
335349
{
336-
resultsSpan[i].Dispose();
350+
resultsSpan[i].Release(store);
337351
}
338352
}
339353
}
340354
finally
341355
{
342356
for (int i = 0; i < arguments.Length; ++i)
343357
{
344-
args[i].Dispose();
358+
args[i].Release(store);
345359
}
346360
}
347-
348361
}
349362

350363
/// <summary>
@@ -444,8 +457,7 @@ Extern IExternal.AsExtern()
444457
internal Function()
445458
{
446459
this.store = null;
447-
this.func.store = 0;
448-
this.func.index = (UIntPtr)0;
460+
this.func = default;
449461
this.Parameters = this.Results = Array.Empty<ValueKind>();
450462
}
451463

@@ -648,7 +660,23 @@ internal static unsafe IntPtr InvokeUntypedCallback(UntypedCallbackDelegate call
648660

649661
for (int i = 0; i < resultsSpan.Length; i++)
650662
{
651-
results[i] = resultsSpan[i].ToValue(caller.Store, resultKinds[i]);
663+
try
664+
{
665+
results[i] = resultsSpan[i].ToValue(caller.Store, resultKinds[i]);
666+
}
667+
catch
668+
{
669+
// Clean-up the previous result values in case an exception occured
670+
// (e.g. when `wasmtime_externref_new` failed), because we will
671+
// return an error in that case and therefore can't pass ownership
672+
// of already allocated result values.
673+
for (int releaseIndex = 0; releaseIndex < i; releaseIndex++)
674+
{
675+
results[releaseIndex].Release(caller.Store);
676+
}
677+
678+
throw;
679+
}
652680
}
653681
}
654682
finally

src/Global.cs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,20 @@ public Global(Store store, ValueKind kind, object? initialValue, Mutability muta
118118
}
119119

120120
var value = Value.FromObject(store, initialValue, Kind);
121-
var error = Native.wasmtime_global_new(store.Context.handle, globalType, in value, out this.global);
122-
GC.KeepAlive(store);
123121

124-
value.Dispose();
122+
try
123+
{
124+
var error = Native.wasmtime_global_new(store.Context.handle, globalType, in value, out this.global);
125+
GC.KeepAlive(store);
125126

126-
if (error != IntPtr.Zero)
127+
if (error != IntPtr.Zero)
128+
{
129+
throw WasmtimeException.FromOwnedError(error);
130+
}
131+
}
132+
finally
127133
{
128-
throw WasmtimeException.FromOwnedError(error);
134+
value.Release(store);
129135
}
130136
}
131137

@@ -139,9 +145,15 @@ public Global(Store store, ValueKind kind, object? initialValue, Mutability muta
139145
Native.wasmtime_global_get(context.handle, this.global, out var v);
140146
GC.KeepAlive(store);
141147

142-
var val = v.ToObject(store);
143-
v.Dispose();
144-
return val;
148+
try
149+
{
150+
var val = v.ToObject(store);
151+
return val;
152+
}
153+
finally
154+
{
155+
v.Release(store);
156+
}
145157
}
146158

147159
/// <summary>
@@ -156,10 +168,16 @@ public void SetValue(object? value)
156168
}
157169

158170
var v = Value.FromObject(store, value, Kind);
159-
Native.wasmtime_global_set(store.Context.handle, this.global, in v);
160-
GC.KeepAlive(store);
161171

162-
v.Dispose();
172+
try
173+
{
174+
Native.wasmtime_global_set(store.Context.handle, this.global, in v);
175+
GC.KeepAlive(store);
176+
}
177+
finally
178+
{
179+
v.Release(store);
180+
}
163181
}
164182

165183
/// <summary>
@@ -300,10 +318,15 @@ public T GetValue()
300318
Native.wasmtime_global_get(context.handle, _global.global, out var v);
301319
GC.KeepAlive(_store);
302320

303-
var result = _converter.Unbox(_store, v.ToValueBox(_store));
304-
v.Dispose();
305-
306-
return result;
321+
try
322+
{
323+
var result = _converter.Unbox(_store, v.ToValueBox(_store));
324+
return result;
325+
}
326+
finally
327+
{
328+
v.Release(_store);
329+
}
307330
}
308331

309332
/// <summary>
@@ -317,12 +340,18 @@ public void SetValue(T value)
317340
throw new InvalidOperationException("The global is immutable and cannot be changed.");
318341
}
319342

320-
using (var v = _converter.Box(value).ToValue(_store, _global.Kind))
343+
var v = _converter.Box(value).ToValue(_store, _global.Kind);
344+
345+
try
321346
{
322347
var context = _store.Context;
323348
Native.wasmtime_global_set(context.handle, _global.global, in v);
324349
GC.KeepAlive(_store);
325350
}
351+
finally
352+
{
353+
v.Release(_store);
354+
}
326355
}
327356

328357
Extern IExternal.AsExtern()

src/Store.cs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -340,45 +340,41 @@ private static class Native
340340

341341
private static readonly Native.Finalizer Finalizer = (p) => GCHandle.FromIntPtr(p).Free();
342342

343-
private readonly ConcurrentDictionary<(ExternKind kind, ulong store, nuint index), object> _externCache = new();
343+
private readonly ConcurrentDictionary<ExternFunc, Function> _externFuncCache = new();
344+
private readonly ConcurrentDictionary<ExternMemory, Memory> _externMemoryCache = new();
345+
private readonly ConcurrentDictionary<ExternGlobal, Global> _externGlobalCache = new();
344346

345347
internal Function GetCachedExtern(ExternFunc @extern)
346348
{
347-
var key = (ExternKind.Func, @extern.store, @extern.index);
348-
349-
if (!_externCache.TryGetValue(key, out var func))
349+
if (!_externFuncCache.TryGetValue(@extern, out var func))
350350
{
351351
func = new Function(this, @extern);
352-
func = _externCache.GetOrAdd(key, func);
352+
func = _externFuncCache.GetOrAdd(@extern, func);
353353
}
354354

355-
return (Function)func;
355+
return func;
356356
}
357357

358358
internal Memory GetCachedExtern(ExternMemory @extern)
359359
{
360-
var key = (ExternKind.Memory, @extern.store, @extern.index);
361-
362-
if (!_externCache.TryGetValue(key, out var mem))
360+
if (!_externMemoryCache.TryGetValue(@extern, out var mem))
363361
{
364362
mem = new Memory(this, @extern);
365-
mem = _externCache.GetOrAdd(key, mem);
363+
mem = _externMemoryCache.GetOrAdd(@extern, mem);
366364
}
367365

368-
return (Memory)mem;
366+
return mem;
369367
}
370368

371369
internal Global GetCachedExtern(ExternGlobal @extern)
372370
{
373-
var key = (ExternKind.Global, @extern.store, @extern.index);
374-
375-
if (!_externCache.TryGetValue(key, out var global))
371+
if (!_externGlobalCache.TryGetValue(@extern, out var global))
376372
{
377373
global = new Global(this, @extern);
378-
global = _externCache.GetOrAdd(key, global);
374+
global = _externGlobalCache.GetOrAdd(@extern, global);
379375
}
380376

381-
return (Global)global;
377+
return global;
382378
}
383379
}
384380
}

0 commit comments

Comments
 (0)