EDIT: я не нашел способа безопасно завершить потоки LuaJ без изменения самого LuaJ. Вот что я придумал, хотя это не работает с LuaJ. Однако его можно легко изменить, чтобы он выполнял свою работу на чистом Lua. Я могу переключиться на привязку Python для Java, поскольку многопоточность LuaJ настолько проблематична.
--- Я придумал следующее, но это не работает с LuaJ ---
Вот возможное решение. Я регистрирую хук с помощью debug.sethook, который срабатывает при событиях "count" (эти события происходят даже в while true do end
). Я также передаю созданный мной пользовательский объект Java "ScriptState", который содержит логический флаг, указывающий, должен ли сценарий завершаться или нет. Объект Java запрашивается в хуке Lua, который выдает ошибку закрытия скрипта, если установлен флаг (редактировать: выдача ошибки фактически не завершает скрипт). Флаг завершения также может быть установлен внутри Lua-скрипта.
Если вы хотите автоматически завершать непрекращающиеся бесконечные циклы, достаточно просто реализовать систему таймеров, которая записывает время последнего вызова ScriptState, а затем автоматически завершает скрипт, если проходит достаточное время без вызова API (редактировать: это работает, только если поток можно прервать). Если вы хотите отключить бесконечные циклы, но не прерывать определенные блокирующие операции, вы можете настроить объект ScriptState, включив в него другую информацию о состоянии, которая позволит вам временно приостановить автоматическое завершение и т. д.
Вот мой interpreter.lua
, который можно использовать для вызова другого скрипта и прерывания его, если/когда это необходимо. Он вызывает методы Java, поэтому он не будет работать без LuaJ (или какой-либо другой библиотеки Lua-Java), если только он не будет модифицирован (редактировать: опять же, его можно легко изменить для работы в чистом 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
Вот класс ScriptState
, в котором хранится флаг и main()
для демонстрации:
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
содержит целевой код Lua для запуска. Используйте переменные среды или параметры для передачи дополнительной информации сценарию, как обычно. Не забудьте использовать JsePlatform.debugGlobals()
вместо JsePlatform.standardGlobals()
, если вы хотите использовать библиотеку debug
в Lua.
EDIT: я только что заметил, что os.exit()
завершает не только сценарий Lua, но и вызывающий процесс. Кажется, это эквивалент System.exit()
. error()
выдаст ошибку, но не приведет к завершению Lua-скрипта. Сейчас я пытаюсь найти решение этой проблемы.
person
xikkub
schedule
07.07.2013