|
@@ -1,208 +1,54 @@
|
|
|
-using System.Threading.Tasks.Sources;
|
|
|
|
|
using Lua.Internal;
|
|
using Lua.Internal;
|
|
|
|
|
+using Lua.Runtime;
|
|
|
|
|
|
|
|
namespace Lua;
|
|
namespace Lua;
|
|
|
|
|
|
|
|
-public sealed class LuaThread : IValueTaskSource<LuaThread.YieldContext>, IValueTaskSource<LuaThread.ResumeContext>
|
|
|
|
|
|
|
+public abstract class LuaThread
|
|
|
{
|
|
{
|
|
|
- struct YieldContext
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- struct ResumeContext
|
|
|
|
|
- {
|
|
|
|
|
- public LuaValue[] Results;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- byte status;
|
|
|
|
|
- LuaState threadState;
|
|
|
|
|
- ValueTask<int> functionTask;
|
|
|
|
|
|
|
+ public abstract LuaThreadStatus GetStatus();
|
|
|
|
|
+ public abstract ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default);
|
|
|
|
|
+ public abstract ValueTask Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default);
|
|
|
|
|
|
|
|
- ManualResetValueTaskSourceCore<ResumeContext> resume;
|
|
|
|
|
- ManualResetValueTaskSourceCore<YieldContext> yield;
|
|
|
|
|
|
|
+ LuaStack stack = new();
|
|
|
|
|
+ FastStackCore<CallStackFrame> callStack;
|
|
|
|
|
|
|
|
- public LuaThreadStatus Status => (LuaThreadStatus)status;
|
|
|
|
|
- public bool IsProtectedMode { get; }
|
|
|
|
|
- public LuaFunction Function { get; }
|
|
|
|
|
|
|
+ internal LuaStack Stack => stack;
|
|
|
|
|
|
|
|
- internal LuaThread(LuaState state, LuaFunction function, bool isProtectedMode)
|
|
|
|
|
|
|
+ public CallStackFrame GetCurrentFrame()
|
|
|
{
|
|
{
|
|
|
- IsProtectedMode = isProtectedMode;
|
|
|
|
|
- threadState = state;
|
|
|
|
|
- Function = function;
|
|
|
|
|
|
|
+ return callStack.Peek();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public async ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
|
|
|
|
|
|
|
+ public ReadOnlySpan<LuaValue> GetStackValues()
|
|
|
{
|
|
{
|
|
|
- context.State.ThreadStack.Push(this);
|
|
|
|
|
- try
|
|
|
|
|
- {
|
|
|
|
|
- switch ((LuaThreadStatus)Volatile.Read(ref status))
|
|
|
|
|
- {
|
|
|
|
|
- case LuaThreadStatus.Normal:
|
|
|
|
|
- Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
|
|
|
|
|
-
|
|
|
|
|
- // first argument is LuaThread object
|
|
|
|
|
- for (int i = 0; i < context.ArgumentCount - 1; i++)
|
|
|
|
|
- {
|
|
|
|
|
- threadState.Push(context.Arguments[i + 1]);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- functionTask = Function.InvokeAsync(new()
|
|
|
|
|
- {
|
|
|
|
|
- State = threadState,
|
|
|
|
|
- ArgumentCount = context.ArgumentCount - 1,
|
|
|
|
|
- ChunkName = Function.Name,
|
|
|
|
|
- RootChunkName = context.RootChunkName,
|
|
|
|
|
- }, buffer[1..], cancellationToken).Preserve();
|
|
|
|
|
-
|
|
|
|
|
- break;
|
|
|
|
|
- case LuaThreadStatus.Suspended:
|
|
|
|
|
- Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
|
|
|
|
|
- yield.SetResult(new());
|
|
|
|
|
- break;
|
|
|
|
|
- case LuaThreadStatus.Running:
|
|
|
|
|
- throw new InvalidOperationException("cannot resume running coroutine");
|
|
|
|
|
- case LuaThreadStatus.Dead:
|
|
|
|
|
- if (IsProtectedMode)
|
|
|
|
|
- {
|
|
|
|
|
- buffer.Span[0] = false;
|
|
|
|
|
- buffer.Span[1] = "cannot resume dead coroutine";
|
|
|
|
|
- return 2;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- throw new InvalidOperationException("cannot resume dead coroutine");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var resumeTask = new ValueTask<ResumeContext>(this, resume.Version);
|
|
|
|
|
-
|
|
|
|
|
- CancellationTokenRegistration registration = default;
|
|
|
|
|
- if (cancellationToken.CanBeCanceled)
|
|
|
|
|
- {
|
|
|
|
|
- registration = cancellationToken.UnsafeRegister(static x =>
|
|
|
|
|
- {
|
|
|
|
|
- var thread = (LuaThread)x!;
|
|
|
|
|
- thread.yield.SetException(new OperationCanceledException());
|
|
|
|
|
- }, this);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try
|
|
|
|
|
- {
|
|
|
|
|
- (var index, var result0, var result1) = await ValueTaskEx.WhenAny(resumeTask, functionTask!);
|
|
|
|
|
-
|
|
|
|
|
- if (index == 0)
|
|
|
|
|
- {
|
|
|
|
|
- var results = result0.Results;
|
|
|
|
|
-
|
|
|
|
|
- buffer.Span[0] = true;
|
|
|
|
|
- for (int i = 0; i < results.Length; i++)
|
|
|
|
|
- {
|
|
|
|
|
- buffer.Span[i + 1] = results[i];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return results.Length + 1;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
|
|
|
|
|
- buffer.Span[0] = true;
|
|
|
|
|
- return 1 + functionTask!.Result;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- catch (Exception ex) when (ex is not OperationCanceledException)
|
|
|
|
|
- {
|
|
|
|
|
- if (IsProtectedMode)
|
|
|
|
|
- {
|
|
|
|
|
- Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
|
|
|
|
|
- buffer.Span[0] = false;
|
|
|
|
|
- buffer.Span[1] = ex.Message;
|
|
|
|
|
- return 2;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- throw;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- finally
|
|
|
|
|
- {
|
|
|
|
|
- registration.Dispose();
|
|
|
|
|
- resume.Reset();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- finally
|
|
|
|
|
- {
|
|
|
|
|
- context.State.ThreadStack.Pop();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return stack.AsSpan();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public async ValueTask Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
|
|
|
|
|
|
|
+ internal Tracebacks GetTracebacks()
|
|
|
{
|
|
{
|
|
|
- if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running)
|
|
|
|
|
- {
|
|
|
|
|
- throw new InvalidOperationException("cannot call yield on a coroutine that is not currently running");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- resume.SetResult(new()
|
|
|
|
|
|
|
+ return new()
|
|
|
{
|
|
{
|
|
|
- Results = context.Arguments.ToArray(),
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- Volatile.Write(ref status, (byte)LuaThreadStatus.Suspended);
|
|
|
|
|
-
|
|
|
|
|
- CancellationTokenRegistration registration = default;
|
|
|
|
|
- if (cancellationToken.CanBeCanceled)
|
|
|
|
|
- {
|
|
|
|
|
- registration = cancellationToken.UnsafeRegister(static x =>
|
|
|
|
|
- {
|
|
|
|
|
- var thread = (LuaThread)x!;
|
|
|
|
|
- thread.yield.SetException(new OperationCanceledException());
|
|
|
|
|
- }, this);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- RETRY:
|
|
|
|
|
- try
|
|
|
|
|
- {
|
|
|
|
|
- await new ValueTask<YieldContext>(this, yield.Version);
|
|
|
|
|
- }
|
|
|
|
|
- catch (Exception ex) when (ex is not OperationCanceledException)
|
|
|
|
|
- {
|
|
|
|
|
- yield.Reset();
|
|
|
|
|
- goto RETRY;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- registration.Dispose();
|
|
|
|
|
- yield.Reset();
|
|
|
|
|
|
|
+ StackFrames = callStack.AsSpan()[1..].ToArray()
|
|
|
|
|
+ };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- YieldContext IValueTaskSource<YieldContext>.GetResult(short token)
|
|
|
|
|
|
|
+ internal void PushCallStackFrame(CallStackFrame frame)
|
|
|
{
|
|
{
|
|
|
- return yield.GetResult(token);
|
|
|
|
|
|
|
+ callStack.Push(frame);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ValueTaskSourceStatus IValueTaskSource<YieldContext>.GetStatus(short token)
|
|
|
|
|
|
|
+ internal void PopCallStackFrame()
|
|
|
{
|
|
{
|
|
|
- return yield.GetStatus(token);
|
|
|
|
|
|
|
+ var frame = callStack.Pop();
|
|
|
|
|
+ stack.PopUntil(frame.Base);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- void IValueTaskSource<YieldContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
|
|
|
|
|
|
|
+ internal void DumpStackValues()
|
|
|
{
|
|
{
|
|
|
- yield.OnCompleted(continuation, state, token, flags);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ResumeContext IValueTaskSource<ResumeContext>.GetResult(short token)
|
|
|
|
|
- {
|
|
|
|
|
- return resume.GetResult(token);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ValueTaskSourceStatus IValueTaskSource<ResumeContext>.GetStatus(short token)
|
|
|
|
|
- {
|
|
|
|
|
- return resume.GetStatus(token);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void IValueTaskSource<ResumeContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
|
|
|
|
|
- {
|
|
|
|
|
- resume.OnCompleted(continuation, state, token, flags);
|
|
|
|
|
|
|
+ var span = GetStackValues();
|
|
|
|
|
+ for (int i = 0; i < span.Length; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ Console.WriteLine($"LuaStack [{i}]\t{span[i]}");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|