NLua / NLua

Bridge between Lua and the .NET.
http://nlua.org
MIT License
2.05k stars 309 forks source link

FeatureRequest: A Top await DoStringAsync() Entrypoint #541

Open personball opened 3 days ago

personball commented 3 days ago

Hi, after I search of 'async await' in issues, with #514 #295 #52

I know how to do a callback when lua invoke a async C# method with ContinueWith.

But, it is really a 'fire and forgot' invocation. The call stack will not back to the top caller.

How can I do a real one await when invoking DoString() ?

Here is codes reproduce it:

        public async Task<(string?, string?)> CreateNextProjectTaskAsync(ProjectTask upstream, string nextTaskType)
        {
            await Task.Delay(1000);
            return (upstream.TaskName, nextTaskType);
        }
        public void CreateNextTask(ProjectTask upstream, string nextTaskType, LuaFunction? callback = null)
        {
            CreateNextProjectTaskAsync(upstream, nextTaskType).ContinueWith(t =>
            {
                var res = t.Result;
                callback?.Call(res.Item1, res.Item2);
            });
        }

        [Fact]
        public async Task HandleTaskFinished()
        {
            var action =
@"
import 'System'
m:CreateNextTask(task,'Test',function(res1,res2)
    print(res1)
    x=res1
    print(res2)
    y=res1
end)
";
            using var lua = new Lua();
            lua.State.Encoding = Encoding.UTF8;
            lua.LoadCLRPackage();
            lua["m"] = this;
            lua["task"] = new ProjectTask(Guid.NewGuid())
            {
                TaskName = "Test"
            };
            var actionResults = lua.DoString(action);
            var x = lua["x"]; //null!! This line run before lua callback!
            //x.ShouldBe("Test");
            var y = lua["y"];
            //y.ShouldBe("Test");

            await Task.Delay(1000);//thread switch to callback invoke

            lua.State.Status.ShouldBe(KeraLua.LuaStatus.OK);

        }

ProjectTask could be any class with a Guid Id and string TaskName.

personball commented 3 days ago

And now, I can use TaskCompletionSource to do some workaround...

  private TaskCompletionSource _taskCompletionSource = new(); // one time per await
...
  public void CreateNextTask(ProjectTask upstream, string nextTaskType, LuaFunction? callback = null)
  {
      CreateNextProjectTaskAsync(upstream, nextTaskType).ContinueWith(t =>
      {
          var res = t.Result;

          callback?.Call(res.Item1, res.Item2);

          _taskCompletionSource.SetResult();
      });
  }

 [Fact]
 public async Task HandleTaskFinished()
 {
  ...
  var actionResults = lua.DoString(action);

 await _taskCompletionSource.Task;// wait until _taskCompletionSource.SetResult(); called.

  var x = lua["x"]; // Test
  x.ShouldBe("Test");
  var y = lua["y"]; // Test
  y.ShouldBe("Test");

  lua.State.Status.ShouldBe(KeraLua.LuaStatus.OK);
}