强制Lua脚本退出

时间:2011-08-02 14:50:44

标签: lua

你如何结束长期运行的Lua脚本?

我有两个线程,一个运行主程序,另一个控制用户提供的Lua脚本。我需要杀死正在运行Lua的线程,但首先我需要脚本退出。

有没有办法强制脚本退出?

我已经读过建议的方法是返回一个Lua异常。但是,并不保证用户的脚本将调用api函数(它可能处于紧张的繁忙循环中)。此外,用户可以使用pcall防止错误导致其脚本退出。

10 个答案:

答案 0 :(得分:18)

你可以使用setjmplongjump,就像Lua库在内部做的那样。这样就可以让你离开pcalls而且不需要连续错误就可以了,防止脚本试图处理你的虚假错误并让你不再执行。 (我不知道这对线程有多好。)

#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;
}

答案 1 :(得分:6)

您可以在程序中的某个位置设置变量,并将其称为forceQuitLuaScript。然后,您使用一个钩子,描述here来运行每个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;
}

答案 2 :(得分:2)

结束脚本的方法是通过调用error来引发错误。但是,如果用户通过pcall调用了脚本,则会捕获此错误。

答案 3 :(得分:2)

好像你可以从外部(从主线程)终止线程,因为lua脚本是用户提供的,你无法发出信号要退出。

如果这不是一个选项,您可以尝试调试API。您可以使用lua_sethook来重新获得控制权,假设您有办法优雅地终止线程中的线程。

答案 4 :(得分:2)

我还没有找到一种方法来干净地杀死执行长期运行的lua脚本的线程,而不依赖于脚本本身的一些干预。以下是我过去采取的一些方法:

  1. 如果脚本长时间运行,很可能是在某个循环中。该脚本可以在每次迭代时检查某个全局变量的值。通过从脚本外部设置此变量,您可以终止该线程。
  2. 您可以使用lua_resume启动该线程。然后可以使用yield()退出脚本。
  3. 您可以提供自己的pcall实现来检查特定类型的错误。然后,该脚本可以使用您的pcall版本可以查看的自定义错误类型调用error():

    function()
        local there_is_an_error = do_something()
        if (there_is_an_error) then
            error({code = 900, msg = "Custom error"})
        end
    end
    

答案 5 :(得分:2)

可能没用,但在我使用的lua(luaplayer或PGELua)中,我退出

os.exit() 

pge.exit()

答案 6 :(得分:1)

如果你使用协同程序来启动线程,你可以使用coroutine.yield()来阻止它。

答案 7 :(得分:1)

你可能想看看 https://github.com/amilamad/preemptive-task-scheduler-for-lua 项目。它的抢先调度程序为lua。 它在钩子里面使用了一个lua_yeild函数。所以你可以暂停你的lua线程。它也使用longjmp,但它更安全。

答案 8 :(得分:0)

会话:破坏();

在你想破坏lua脚本的地方使用这个单行代码。

答案 9 :(得分:0)

lua_KFunction cont(lua_State* L);
int my_yield_with_res(lua_State* L, int res) {
    cout << " my_yield_with_res \n" << endl;
    return lua_yieldk(L, 0, lua_yield(L, res), cont(L));/* int lua_yieldk(lua_State * L, int res, lua_KContext ctx, lua_KFunction k);
    Приостанавливает выполнение сопрограммы(поток). Когда функция C вызывает lua_yieldk, работающая
    сопрограмма приостанавливает свое выполнение и вызывает lua_resume, которая начинает возврат данной сопрограммы.
    Параметр res - это число значений из стека, которые будут переданы в качестве результатов в lua_resume.
    Когда сопрограмма снова возобновит выполнение, Lua вызовет заданную функцию продолжения k для продолжения выполнения
    приостановленной C функции(смотрите §4.7). */
};
int hookFunc(lua_State* L, lua_Debug* ar) {
    cout << " hookFunc \n" << endl;
    return my_yield_with_res(L, 0);// хук./
};

lua_KFunction cont(lua_State* L) {// функция продолжения.
    cout << " hooh off \n" << endl;
    lua_sethook(L, (lua_Hook)hookFunc, LUA_MASKCOUNT, 0);// отключить хук foo.
    return 0;
};

struct Func_resume {
    Func_resume(lua_State* L, const char* funcrun, unsigned int Args) : m_L(L), m_funcrun(funcrun), m_Args(Args) {}
    //имена функций, кол-во агрументов.
private:
    void func_block(lua_State* L, const char* functionName, unsigned int Count, unsigned int m_Args) {
        lua_sethook(m_L, (lua_Hook)hookFunc, LUA_MASKCOUNT, Count); //вызов функции с заданной паузой.
        if (m_Args == 0) {
            lua_getglobal(L, functionName);// получить имя функции.
            lua_resume(L, L, m_Args);
        }
        if (m_Args != 0) {
            int size = m_Args + 1;
            lua_getglobal(L, functionName);
            for (int i = 1; i < size; i++) {
                lua_pushvalue(L, i);
            }
            lua_resume(L, L, m_Args);
        }
    };
public:
    void Update(float dt) {
        unsigned int Count = dt * 100.0;// Время работы потока.
        func_block(m_L, m_funcrun, Count, m_Args);
    };
    ~Func_resume() {}
private:
    lua_State* m_L;
    const char* m_funcrun; // имя функции.
    unsigned int m_Count;// число итерации.
    unsigned int m_Args;
};

const char* LUA = R"(
function main(y) 
  --print(" func main arg, a = ".. a.." y = ".. y)      
for i = 1, y do
  print(" func main count = ".. i)      
  end
end
)";
int main(int argc, char* argv[]) {
    lua_State* L = luaL_newstate();/*Функция создает новое Lua состояние. */
    luaL_openlibs(L);
    luaL_dostring(L, LUA);
    //..pushlua(L, 12);
    pushlua(L, 32);
    //do {
        Func_resume func_resume(L, "main", 2);
        func_resume.Update(1.7);
        lua_close(L);
//  } while (LUA_OK != lua_status(L)); // Пока  поток не завершен.


    return 0;
};