while tipos sentencia for ejemplos ciclos java multithreading lua interrupt luaj

sentencia - tipos de for en java



Lua/Java/LuaJ-Manejo o interrupciĆ³n de bucles e hilos infinitos (4)

Estoy usando LuaJ para ejecutar scripts Lua creados por el usuario en Java. Sin embargo, ejecutar un script de Lua que nunca retorna hace que el hilo de Java se congele. Esto también hace que el hilo sea ininterrumpible. Ejecuto el script de Lua con:

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

badscript.lua contiene while true do end .

Me gustaría poder terminar automáticamente las secuencias de comandos que están bloqueadas en bucles inflexibles y también permitir que los usuarios terminen manualmente sus scripts Lua mientras se ejecutan. He leído sobre debug.sethook y pcall , aunque no estoy seguro de cómo los usaré para mis propósitos. También he oído que sandboxing es una mejor alternativa, aunque eso está un poco fuera de mi alcance.

Esta pregunta también podría extenderse solo a los hilos de Java. No he encontrado ninguna información definitiva sobre la interrupción de los hilos de Java atrapados en un while (true); .

La demo en línea de Lua fue muy prometedora, pero parece que la detección y terminación de los scripts "malos" se realiza en el script CGI y no en Lua. ¿Sería capaz de usar Java para llamar a un script CGI que a su vez llama al script Lua? Sin embargo, no estoy seguro de que eso permita a los usuarios terminar manualmente sus scripts. Perdí el enlace para el código fuente de la demo de Lua, pero lo tengo a mano. Esta es la línea mágica:

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

¿Alguien me puede apuntar en la dirección correcta?

Algunas fuentes:


Nunca antes había usado Luaj, pero ¿no podrías poner tu línea?

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

¿En un nuevo hilo propio, que luego puede terminar desde el hilo principal?

Esto requeriría que usted haga algún tipo de subproceso de supervisor (clase) y le pase cualquier secuencia de comandos iniciada para supervisar y, finalmente, finalizar si no terminan por sí mismos.


Luché con el mismo problema y, después de investigar en la implementación de la biblioteca de depuración, creé una solución similar a la propuesta por David Lewis, pero lo hice al proporcionar mi propia 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 {} }

Simplemente ejecute su script desde dentro de un nuevo hilo y establezca interrupted en true para detener la ejecución. La excepción se encapsulará como la causa de un LuaError cuando se lanza.


Hay problemas, pero esto ayuda mucho a responder tu pregunta.

La siguiente prueba de concepto demuestra un nivel básico de espacio aislado y regulación del código de usuario arbitrario. Ejecuta ~ 250 instrucciones de ''entrada del usuario'' mal diseñado y luego descarta la corutina. Puede usar un mecanismo como el de esta respuesta para consultar Java y ceder condicionalmente dentro de una función de enlace, en lugar de ceder cada vez.

SandboxTest.java:

public static void main(String[] args) { Globals globals = JsePlatform.debugGlobals(); LuaValue chunk = globals.loadfile("res/test.lua"); chunk.call(); }

res / test.lua:

function sandbox(fn) -- read script and set the environment f = loadfile(fn, "t") debug.setupvalue(f, 1, {print = print}) -- create a coroutine and have it yield every 50 instructions local co = coroutine.create(f) debug.sethook(co, coroutine.yield, "", 50) -- demonstrate stepped execution, 5 ''ticks'' for i = 1, 5 do print("tick") coroutine.resume(co) end end sandbox("res/badfile.lua")

res / badfile.lua:

while 1 do print("", "badfile") end

Desafortunadamente, si bien el flujo de control funciona como se pretendía, algo en la forma en que la corutina "abandonada" debería recoger la basura no está funcionando correctamente. El correspondiente LuaThread en Java se cuelga para siempre en un bucle de espera, manteniendo vivo el proceso. Detalles aquí:

¿Cómo puedo abandonar LuaJ coroutine LuaThread?


EDITAR: No he encontrado ninguna manera de terminar de forma segura los hilos de LuaJ sin modificar LuaJ. Lo siguiente fue lo que se me ocurrió, aunque no funciona con LuaJ. Sin embargo, puede modificarse fácilmente para hacer su trabajo en Lua puro. Es posible que esté cambiando a un enlace de Python para Java, ya que el enhebrado de LuaJ es muy problemático.

--- Se me ocurrió lo siguiente, pero no funciona con LuaJ ---

Aquí hay una posible solución. Registro un gancho con debug.sethook que se activa en los eventos de "conteo" (estos eventos ocurren incluso en un while true do end ). También paso un objeto Java "ScriptState" personalizado que creé y que contiene un indicador booleano que indica si el script debe finalizar o no. El objeto Java se consulta en el enlace Lua, que arrojará un error para cerrar el script si se establece el indicador (edit: lanzar un error no termina realmente el script) . El indicador de finalización también se puede establecer desde dentro del script Lua.

Si desea terminar automáticamente bucles infinitos inflexibles, es bastante sencillo implementar un sistema de temporizador que registre la última vez que se realizó una llamada al ScriptState, y luego finalizar automáticamente el script si transcurre suficiente tiempo sin una llamada API (editar: esto solo funciona si el hilo puede ser interrumpido) . Si desea eliminar infinitos bucles pero no interrumpir ciertas operaciones de bloqueo, puede ajustar el objeto ScriptState para incluir otra información de estado que le permita pausar temporalmente la terminación automática, etc.

Aquí está mi interpreter.lua que se puede usar para llamar a otro guión e interrumpirlo si es necesario. Realiza llamadas a métodos Java para que no se ejecute sin LuaJ (o alguna otra biblioteca Lua-Java) a menos que se modifique (editar: una vez más, se puede modificar fácilmente para que funcione en Lua puro) .

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

Aquí está la clase ScriptState que almacena la bandera y una main() para demostrar:

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 contiene el código Lua objetivo que se ejecutará. Use variables de entorno o parámetros para pasar información adicional al script como de costumbre. Recuerde utilizar JsePlatform.debugGlobals() lugar de JsePlatform.standardGlobals() si desea usar la biblioteca de debug en Lua.

EDITAR: Me acabo de dar cuenta de que os.exit() no solo finaliza el script Lua sino también el proceso de llamada. Parece ser el equivalente de System.exit() . error() lanzará un error pero no hará que la secuencia de comandos Lua termine. Estoy tratando de encontrar una solución para esto ahora.