1

I'm using NLua to run Lua script in my app. I need to implement ability to terminate script that runs in separate thread at any time, for example user press "Stop" button and script must terminate immediately. I've read about SetDebugHook and tried to Close Lua State and call Error on state, but I always get AccessViolationException.

I've tried

Lua env = new Lua(); // created in main thread
env.DoString(); // called in second thread

// Called in main thread
public void Stop()
{
    env.Close(); // Didn't work. AccessViolationException
    env.State.Close(); // Didn't work. AccessViolationException
    env.State.Error("err"); // Didn't work. AccessViolationException
}

Tried to synchronize threads with lock

lock (locker)
{
    if (env.IsExecuting)
        env.Close();
}

Same issue. AccessViolationException

Thanks.

  • You need to post the code that you tried. – Joseph Sible-Reinstate Monica Jun 19 '20 at 04:36
  • I've posted some code, but none of them work, same issue everytime, AccessViolationException – craftersmine Jun 19 '20 at 12:42
  • I had a similar need using `Nlua` i set up a variable that i would set to true when the `ABORT` button was pressed by the operator `env["ABORT"] = this.abort` this variable was check in the codes lowest function where the code spent most of it's time. the press was not detected "immediately" but between 0-250ms after, this specific system also was equipped with a hardware emergency stop that cut power from the system when pressed, all systems that are required to stop should have a hardware stop as software will never be 100% reliable, is the input device damage, is the pc frozen, etc. – Nifim Jun 19 '20 at 16:15
  • I need to terminate NLua script execution but without declaring any variables and checks withing the Lua code that executing now, pure C#. So if code executes and user presses "Stop" button, script should immediatelly terminate, it can be terminated with latency but preferable to terminate as soon as it can – craftersmine Jun 19 '20 at 16:45

1 Answers1

1

This method works reasonably well, using the lua_sethook to check for signal to abort before executing each line of lua code:

public partial class NluaThreading : Form
{
    private Lua state;
    private bool abort;
    public NluaThreading()
    {
        InitializeComponent();
    }

    private void Start_Click(object sender, EventArgs e)
    {
        state = new Lua();
        state.SetDebugHook(KeraLua.LuaHookMask.Line, 0);
        state.DebugHook += State_DebugHook;

        abort = true; //force abort after first debughook event

        new Thread(DoLua).Start();
    }

    private void State_DebugHook(object sender, NLua.Event.DebugHookEventArgs e)
    {
        if (abort)
        {
            Lua l = (Lua)sender;
            l.State.Error("Execution manually aborted");
        }
    }
    private void DoLua()
    {
        try
        {
            state.DoString("while(true) do end");
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message, "DoLua", MessageBoxButtons.OK);
        }
    }
}

this ofcourse comes at the cost of some added overhead for every line, to reduce that you can change the hook one of the other values.


Another option is to use tokens that the lua thread would watch and then abort as needed, this method does require some handling within the lua script:

public partial class NluaThreading : Form
{
    internal class Tokens
    {
        public bool abort = false;
    }

    private Lua state;
    private Tokens tokens;

    public NluaThreading()
    {
       InitializeComponent();

       state = new Lua();
       tokens = new Tokens();
       state["tokens"] = tokens; //now the tokens are visible inside the lua
                                 //environment and will reflect changes we make 
                                 //from the main thread
    }

    private void Start_Click(object sender, EventArgs e)
    {
        if (!state.IsExecuting)
        {
            tokens.abort = false;
            new Thread(DoLua).Start();
        }
    }

    private void Stop_Click(object sender, EventArgs e) => tokens.abort = true;

    private void DoLua() => state.DoString("repeat print(tokens.abort) until(tokens.abort); print(tokens.abort)");
}

Now often your lua execution will be more complex, containing many nested loops, and in those cases you can implement a function in lua to check the tokens and throw an error when the token is true:

function checkTokens()
    if tokens.abort then
        error('Execution manually aborted')
    end
end

with that loaded into the lua state we should make some changes to the DoLua function:

private void DoLua()
{
    try
    {
        state.DoString("while(true) do print(tokens.abort); checkTokens(); end");
    }
    catch(Exception e)
    {
        MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK);
    }
}
Nifim
  • 4,758
  • 2
  • 12
  • 31
  • I don't need to terminate thread itself, I need to terminate script only that runs in that script, thread will exit normally after script will terminate. And no, this is not a factory production facility, it is just app which emulates environment for script. Also, I need to implement it without any Lua code as much as I can. But Lua code executes same way as you wrote – craftersmine Jun 20 '20 at 02:48
  • updated answer to provide an option with no lua code needed. – Nifim Jun 20 '20 at 06:22
  • Sorry for long response, I was working on something else during this period, but that did helped me and doing exactly what I've expected (I'm using DebugHooks). Thanks – craftersmine Jun 21 '20 at 07:46