Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions tests/Lua.Tests/ExceptionLifetimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,25 @@ public void DiscardedExecutionErrorDoesNotRootPayload(bool asynchronous, bool re
[MethodImpl(MethodImplOptions.NoInlining)]
static WeakReference FailExecution(LuaState state, bool asynchronous, bool readMessage)
{
var suspension = new ManualSuspension();
var payload = new object();
state.Environment["payload"] = LuaValue.FromObject(payload);
state.Environment["pause"] = new LuaFunction(
async (context, _) =>
{
if (asynchronous)
await Task.Yield();
await suspension;
return context.Return();
}
);
try
{
state
var operation = state
.DoStringAsync("local value=payload;payload=nil;pause();error(value)")
.AsTask()
.GetAwaiter()
.GetResult();
.AsTask();
if (asynchronous)
suspension.Resume();
operation.GetAwaiter().GetResult();
Assert.Fail("Expected a Lua error.");
}
catch (LuaRuntimeException exception)
Expand All @@ -68,22 +70,22 @@ public void DiscardedDirectCallErrorDoesNotRootPayload(bool asynchronous)
[MethodImpl(MethodImplOptions.NoInlining)]
static WeakReference FailDirectCall(LuaState state, bool asynchronous)
{
var suspension = new ManualSuspension();
var payload = new object();
var function = new LuaFunction(
async (context, _) =>
{
if (asynchronous)
await Task.Yield();
await suspension;
throw new LuaRuntimeException(context.State, context.GetArgument(0));
}
);
try
{
state
.CallAsync(function, [LuaValue.FromObject(payload)])
.AsTask()
.GetAwaiter()
.GetResult();
var operation = state.CallAsync(function, [LuaValue.FromObject(payload)]).AsTask();
if (asynchronous)
suspension.Resume();
operation.GetAwaiter().GetResult();
Assert.Fail("Expected a Lua error.");
}
catch (LuaRuntimeException) { }
Expand All @@ -101,6 +103,7 @@ public void DiscardedMetamethodErrorDoesNotRootCapturedPayload(bool asynchronous
[MethodImpl(MethodImplOptions.NoInlining)]
static WeakReference FailMetamethod(LuaState state, bool asynchronous)
{
var suspension = new ManualSuspension();
var payload = new object();
var table = new LuaTable
{
Expand All @@ -110,7 +113,7 @@ static WeakReference FailMetamethod(LuaState state, bool asynchronous)
async (context, _) =>
{
if (asynchronous)
await Task.Yield();
await suspension;
GC.KeepAlive(payload);
throw new LuaRuntimeException(context.State, "failure");
}
Expand All @@ -119,7 +122,10 @@ static WeakReference FailMetamethod(LuaState state, bool asynchronous)
};
try
{
state.AddAsync(table, 1).AsTask().GetAwaiter().GetResult();
var operation = state.AddAsync(table, 1).AsTask();
if (asynchronous)
suspension.Resume();
operation.GetAwaiter().GetResult();
Assert.Fail("Expected a Lua error.");
}
catch (LuaRuntimeException) { }
Expand Down
24 changes: 10 additions & 14 deletions tests/Lua.Tests/ExecutionContextLifetimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,33 @@ public class ExecutionContextLifetimeTests
public void CompletedExecutionDoesNotRootDisposedState(bool asynchronous, bool failure)
{
var reference = RunAndDispose(asynchronous, failure);
// A completed async continuation may still be unwinding on another thread.
Assert.That(
() =>
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
return reference.IsAlive;
},
Is.False.After(1000, 10),
"A pooled VM context retained its Lua state."
);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Assert.That(reference.IsAlive, Is.False, "A pooled VM context retained its Lua state.");
}

[MethodImpl(MethodImplOptions.NoInlining)]
static WeakReference RunAndDispose(bool asynchronous, bool failure)
{
using var state = LuaState.Create();
var suspension = new ManualSuspension();
state.Environment["callback"] = new LuaFunction(
async (context, _) =>
{
if (asynchronous)
await Task.Yield();
await suspension;
if (failure)
throw new LuaRuntimeException(context.State, "failure");
return context.Return(42);
}
);
try
{
state.DoStringAsync("return callback()").AsTask().GetAwaiter().GetResult();
var operation = state.DoStringAsync("return callback()").AsTask();
if (asynchronous)
suspension.Resume();
operation.GetAwaiter().GetResult();
Assert.That(failure, Is.False);
}
catch (LuaRuntimeException) when (failure) { }
Expand Down
25 changes: 25 additions & 0 deletions tests/Lua.Tests/ManualSuspension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Runtime.CompilerServices;

namespace Lua.Tests;

sealed class ManualSuspension : INotifyCompletion
{
Action? continuation;

public ManualSuspension GetAwaiter() => this;

public bool IsCompleted => false;

public void GetResult() { }

public void OnCompleted(Action continuation) => this.continuation = continuation;

// Return from the continuation before callers assert collectibility.
[MethodImpl(MethodImplOptions.NoInlining)]
public void Resume()
{
var callback = continuation!;
continuation = null;
callback();
}
}
Loading