programar - Forzando un script Lua para salir
operadores en lua (9)
¿Cómo terminas un script de Lua de larga duración?
Tengo dos subprocesos, uno ejecuta el programa principal y el otro controla un script Lua proporcionado por el usuario. Necesito matar el hilo que está ejecutando Lua, pero primero necesito la secuencia de comandos para salir.
¿Hay una manera de forzar un script para salir?
He leído que el enfoque sugerido es devolver una excepción Lua. Sin embargo, no se garantiza que el script del usuario alguna vez llame a una función de api (podría estar en un bucle de ocupado apretado). Además, el usuario podría evitar que los errores causen que su script se pcall
al usar un pcall
.
Es posible que desee echar un vistazo a github.com/amilamad/preemptive-task-scheduler-for-lua project. Su planificador preventivo para lua. Utiliza una función lua_yeild dentro del gancho. Así que puedes suspender tu hilo lua. También utiliza longjmp en el interior, pero es mucho más seguro.
La forma de finalizar un script es generar un error llamando al error
. Sin embargo, si el usuario ha llamado al script a través de pcall
, este error se pcall
.
No he encontrado una forma de eliminar de forma limpia un hilo que está ejecutando un script lua de larga ejecución sin depender de alguna intervención del propio script. Aquí hay algunos enfoques que he tomado en el pasado:
- Si el script es de larga ejecución, lo más probable es que esté en algún bucle. El script puede verificar el valor de alguna variable global en cada iteración. Al configurar esta variable desde fuera del script, puede terminar el hilo.
- Puedes iniciar el hilo usando lua_resume. El script puede luego salir usando el rendimiento ().
Podría proporcionar su propia implementación de pcall que verifique un tipo específico de error. La secuencia de comandos podría luego llamar error () con un tipo de error personalizado que su versión de pcall podría ver:
function() local there_is_an_error = do_something() if (there_is_an_error) then error({code = 900, msg = "Custom error"}) end end
Parece que podría terminar el hilo externamente (desde su hilo principal) ya que el script lua es suministrado por el usuario y no puede indicar que se cierre.
Si esa no es una opción, puede probar la API de depuración. Podría usar lua_sethook
para permitirle recuperar el control asumiendo que tiene una manera de terminar con gracia su hilo en el gancho.
Podría establecer una variable en algún lugar de su programa y llamarlo algo como forceQuitLuaScript
. Luego, utiliza un gancho, que se describe here para ejecutar cada n
instrucciones. Después de n
instrucciones, ejecutará su gancho que solo verifica si forceQuitLuaScript
está configurado, y si es necesario hacer alguna limpieza, debe hacer y eliminar el hilo.
Edición: Este es un ejemplo barato de cómo podría funcionar, solo que se trata de un solo hilo. Esto es solo para ilustrar cómo puede manejar pcall y tales:
#include <stdlib.h>
#include "lauxlib.h"
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!/n", countdown);
}
else
{
// From now on, as soon as a line is executed, error
// keep erroring until you''re script reaches the top
lua_sethook(L, hook, LUA_MASKLINE, 0);
luaL_error(L, "");
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
// Infinitely recurse into pcalls
luaL_dostring(L, "function test() pcall(test) print ''recursing'' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
Podría usar setjmp
y longjump
, al igual que la biblioteca de Lua lo hace internamente. Eso lo sacará de los pcalls
y todo pcalls
bien sin la necesidad de errores continuos, evitando que el script intente manejar sus errores falsos y aún así salga de la ejecución. (No tengo idea de lo bien que esto juega con los hilos sin embargo.)
#include <stdio.h>
#include <setjmp.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
jmp_buf place;
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!/n", countdown);
}
else
{
longjmp(place, 1);
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
if (setjmp(place) == 0)
luaL_dostring(L, "function test() pcall(test) print ''recursing'' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
Si está utilizando coroutines para iniciar los subprocesos, tal vez podría usar coroutine.yield()
para detenerlo.
posiblemente inútil, pero en el lua que uso (luaplayer o PGELua), salgo con
os.exit()
o
pge.exit()
sesión: destruir ();
Utilice este código de línea única en el lugar donde desea destruir el script lua.