EDIT: I have not found a way to safely terminate LuaJ threads without modifying LuaJ itself. The following was what I came up with, although it does not work with LuaJ. However, it can be easily modified to do its job in pure Lua. I can switch to Python bindings for Java, as LuaJ threads are so problematic.
--- I came up with the following, but it does not work with LuaJ ---
Here is a possible solution. I register a hook with debug.sethook that fires on "count" events (these events occur even in while true do end ). I also pass in my own Java ScriptState object that I created that contains a boolean flag indicating whether the script should complete or not. The Java object is requested in a Lua hook , which throws an error to close the script if the flag is set (change: throwing an error does not actually end the script) . The completion flag can also be set from the Lua script.
If you want to automatically end infinite infinite loops, it is simple enough to implement a timer system that records the last time the call was made in ScriptState and then automatically terminates the script if enough time passes without calling the API (edit: this only works in in case the thread may be interrupted) . If you want to kill endless loops but not interrupt some locking operations, you can configure the ScriptState to include other status information that allows you to temporarily suspend auto-completion, etc.
Here is my interpreter.lua , which can be used to call another script and interrupt it if / when necessary. It calls Java methods, so it will not work without LuaJ (or some other Lua-Java library) unless it changes (edit: again, it can easily be modified to work in pure Lua) .
function hook_line(e) if jthread:getDone() then -- I saw someone else use error(), but an infinite loop still seems to evade it. -- os.exit() seems to take care of it well. os.exit() end end function inithook() -- the hook will run every 100 million instructions. -- the time it takes for 100 million instructions to occur -- is based on computer speed and the calling environment debug.sethook(hook_line, "", 1e8) local ret = dofile(jLuaScript) debug.sethook() return ret end args = { ... } if jthread == nil then error("jthread object is nil. Please set it in the Java environment.",2) elseif jLuaScript == nil then error("jLuaScript not set. Please set it in the Java environment.",2) else local x,y = xpcall(inithook, debug.traceback) end
Here's the ScriptState class that saves the flag and main() to demonstrate:
public class ScriptState { private AtomicBoolean isDone = new AtomicBoolean(true); public boolean getDone() { return isDone.get(); } public void setDone(boolean v) { isDone.set(v); } public static void main(String[] args) { Thread t = new Thread() { public void run() { System.out.println("J: Lua script started."); ScriptState s = new ScriptState(); Globals g = JsePlatform.debugGlobals(); g.set("jLuaScript", "res/main.lua"); g.set("jthread", CoerceJavaToLua.coerce(s)); try { g.loadFile("res/_interpreter.lua").call(); } catch (Exception e) { System.err.println("There was a Lua error!"); e.printStackTrace(); } } }; t.start(); try { t.join(); } catch (Exception e) { System.err.println("Error waiting for thread"); } System.out.println("J: End main"); } }
res/main.lua contains the target Lua code to run. Use environment variables or parameters to pass additional information to the script, as usual. Remember to use JsePlatform.debugGlobals() instead of JsePlatform.standardGlobals() if you want to use the debug library in Lua.
EDIT: I just noticed that os.exit() not only terminates the Lua script, but also calls the process. This seems to be the equivalent of System.exit() . error() will throw an error, but will not end the Lua script. I am trying to find a solution for this now.