Search code examples
javamultithreadingluainterruptluaj

Lua / Java / LuaJ - Handling or Interrupting Infinite Loops and Threads


I'm using LuaJ to run user-created Lua scripts in Java. However, running a Lua script that never returns causes the Java thread to freeze. This also renders the thread uninterruptible. I run the Lua script with:

JsePlatform.standardGlobals().loadFile("badscript.lua").call();

badscript.lua contains while true do end.

I'd like to be able to automatically terminate scripts which are stuck in unyielding loops and also allow users to manually terminate their Lua scripts while they are running. I've read about debug.sethook and pcall, though I'm not sure how I'd properly use them for my purposes. I've also heard that sandboxing is a better alternative, though that's a bit out of my reach.

This question might also be extended to Java threads alone. I've not found any definitive information on interrupting Java threads stuck in a while (true);.

The online Lua demo was very promising, but it seems the detection and termination of "bad" scripts is done in the CGI script and not Lua. Would I be able to use Java to call a CGI script which in turn calls the Lua script? I'm not sure that would allow users to manually terminate their scripts, though. I lost the link for the Lua demo source code but I have it on hand. This is the magic line:

tee -a $LOG | (ulimit -t 1 ; $LUA demo.lua 2>&1 | head -c 8k)

Can someone point me in the right direction?

Some sources:


Solution

  • I struggled with the same issue and after some digging through the debug library's implementation, I created a solution similar to the one proposed by David Lewis, but did so by providing my own DebugLibrary:

    package org.luaj.vm2.lib;
    
    import org.luaj.vm2.LuaValue;
    import org.luaj.vm2.Varargs;
    
    public class CustomDebugLib extends DebugLib {
        public boolean interrupted = false;
    
        @Override
        public void onInstruction(int pc, Varargs v, int top) {
            if (interrupted) {
                throw new ScriptInterruptException();
            }
            super.onInstruction(pc, v, top);
        }
    
        public static class ScriptInterruptException extends RuntimeException {}
    }
    

    Just execute your script from inside a new thread and set interrupted to true to stop the execution. The exception will be encapsulated as the cause of a LuaError when thrown.