Выключение Lua script для выхода
Как вы заканчиваете длинный Lua script?
У меня есть два потока: один запускает основную программу, а другой управляет пользователем Lua script. Мне нужно убить поток, который запускает Lua, но сначала мне нужно script выйти.
Есть ли способ принудительно выйти из script?
Я читал, что предлагаемый подход заключается в возврате исключения Lua. Однако он не гарантирует, что пользователь script когда-либо вызовет функцию api (он может находиться в замкнутом цикле занятости). Кроме того, пользователь может предотвратить ошибки, вызвавшие его script, с помощью pcall
.
Ответы
Ответ 1
Вы можете использовать setjmp
и longjump
, как и внутренняя библиотека Lua. Это избавит вас от pcalls
и просто отлично, без необходимости постоянной ошибки, не позволяя script пытаться обработать ваши фиктивные ошибки и все равно вывести вас из исполнения. (Я понятия не имею, насколько хорошо это играет с потоками.)
#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;
}
Ответ 2
Вы можете установить переменную где-нибудь в своей программе и называть ее чем-то вроде forceQuitLuaScript
. Затем вы используете крючок, описанный здесь, чтобы запускать все инструкции n
. После инструкций n
он запустит ваш крючок, который просто проверяет, установлен ли forceQuitLuaScript
, и если он выполняет какую-либо очистку, вам нужно сделать и убить поток.
Изменить: вот дешевый пример того, как он может работать, только однопоточное. Это просто, чтобы проиллюстрировать, как вы можете обращаться с pcall и т.д.:
#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;
}
Ответ 3
Способ завершения script заключается в том, чтобы вызвать ошибку, вызвав error
. Однако, если пользователь вызвал script через pcall
, тогда эта ошибка будет обнаружена.
Ответ 4
Кажется, что вы можете завершить поток извне (из основного потока), так как lua script предоставляется пользователю, и вы не можете сигнализировать ему о выходе.
Если это не вариант, вы можете попробовать API отладки. Вы можете использовать lua_sethook
, чтобы вы могли восстановить контроль, предполагая, что у вас есть способ изящно прекратить поток в hook.
Ответ 5
Я не нашел способ для чистого уничтожения потока, выполняющего длинный lua script, не полагаясь на какое-либо вмешательство самого script. Вот несколько подходов, которые я использовал в прошлом:
- Если script работает долго, это, скорее всего, в некотором цикле. script может проверять значение некоторой глобальной переменной на каждой итерации. Установив эту переменную извне script, вы можете завершить поток.
- Вы можете запустить поток, используя lua_resume. script может выйти с помощью yield().
-
Вы можете предоставить свою собственную реализацию pcall, которая проверяет наличие определенного типа ошибок. После этого script может вызвать ошибку() с настраиваемым типом ошибок, который ваша версия pcall могла бы отслеживать:
function()
local there_is_an_error = do_something()
if (there_is_an_error) then
error({code = 900, msg = "Custom error"})
end
end
Ответ 6
возможно бесполезно, но в lua я использую (luaplayer или PGELua), я выхожу с
os.exit()
или
pge.exit()
Ответ 7
Если вы используете сопрограммы для запуска потоков, вы можете использовать coroutine.yield()
, чтобы остановить его.
Ответ 8
сессии: уничтожить();
Используйте этот код одной строки, где вы хотите уничтожить lua script.