Принудительный выход из сценария Lua - PullRequest
27 голосов
/ 02 августа 2011

Как закончить долго работающий скрипт Lua?

У меня есть два потока, один запускает основную программу, а другой управляет предоставленным пользователем скриптом Lua.Мне нужно убить поток, в котором работает Lua, но сначала мне нужен скрипт для выхода.

Есть ли способ заставить скрипт завершиться?

Я читал, что предложенный подходвернуть исключение Lua.Тем не менее, не гарантируется, что пользовательский скрипт когда-либо вызовет функцию API (это может быть в тесном цикле занятости).Кроме того, пользователь может предотвратить появление сценария из-за ошибок с помощью pcall.

Ответы [ 9 ]

18 голосов
/ 16 августа 2011

Вы можете использовать setjmp и longjump, как это делает внутренняя библиотека 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;
}
6 голосов
/ 15 августа 2011

Вы можете установить переменную где-нибудь в вашей программе и назвать ее как 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;
}
2 голосов
/ 11 января 2012

возможно бесполезно, но в lua, который я использую (luaplayer или PGELua), я выхожу с

os.exit() 

или

pge.exit()
2 голосов
/ 16 августа 2011

Я не нашел способа полностью убить поток, который выполняет долго выполняющийся скрипт lua, не полагаясь на некоторое вмешательство самого скрипта. Вот несколько подходов, которые я использовал в прошлом:

  1. Если скрипт долго выполняется, он, скорее всего, в каком-то цикле. Скрипт может проверять значение некоторой глобальной переменной на каждой итерации. Установив эту переменную вне скрипта, вы можете прекратить поток.
  2. Вы можете запустить поток, используя lua_resume. Затем сценарий может выйти с помощью yield ().
  3. Вы можете предоставить собственную реализацию pcall, которая проверяет наличие определенного типа ошибок. Затем скрипт может вызвать error () с пользовательским типом ошибки, который может отслеживать ваша версия pcall:

    function()
        local there_is_an_error = do_something()
        if (there_is_an_error) then
            error({code = 900, msg = "Custom error"})
        end
    end
    
2 голосов
/ 02 августа 2011

Похоже, что вы можете завершить поток извне (из основного потока), так как скрипт lua предоставляется пользователем, и вы не можете дать ему сигнал на выход.

Если это не вариант, вымог попробовать API отладки.Вы можете использовать lua_sethook, чтобы позволить вам восстановить контроль, предполагая, что у вас есть способ изящно завершить вашу нить в ловушке.

2 голосов
/ 02 августа 2011

Чтобы завершить сценарий, нужно вызвать ошибку, вызвав error.Однако если пользователь вызвал скрипт через pcall, то эта ошибка будет обнаружена.

1 голос
/ 17 января 2018

Возможно, вы захотите взглянуть на https://github.com/amilamad/preemptive-task-scheduler-for-lua проект. его упреждающий планировщик для lua. Он использует функцию lua_yeild внутри хука. Таким образом, вы можете приостановить свою нить lua. Он также использует longjmp внутри, но это намного безопаснее.

1 голос
/ 02 августа 2011

Если вы используете сопрограммы для запуска потоков, вы можете использовать coroutine.yield(), чтобы остановить его.

0 голосов
/ 21 мая 2014

сессия: уничтожить ();

Используйте этот однострочный код там, где вы хотите уничтожить скрипт lua.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...