web-dev-qa-db-ja.com

Luaスクリプトを強制的に終了させる

長時間実行されているLuaスクリプトをどのように終了しますか?

2つのスレッドがあります。1つはメインプログラムを実行し、もう1つはユーザーが指定したLuaスクリプトを制御します。 Luaを実行しているスレッドを強制終了する必要がありますが、最初にスクリプトを終了する必要があります。

スクリプトを強制的に終了させる方法はありますか?

提案されたアプローチはLua例外を返すことであることを読みました。ただし、ユーザーのスクリプトがapi関数を呼び出すことは保証されていません(タイトなビジーループになっている可能性があります)。さらに、ユーザーはpcallを使用して、エラーによってスクリプトが終了するのを防ぐことができます。

27
deft_code

Luaライブラリが内部で使用するのと同じように、setjmplongjumpを使用できます。これにより、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;
}
18
Alex

プログラムのどこかに変数を設定して、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;
}
6
Alex

Luaスクリプトはユーザーが提供し、終了するように通知できないため、(メインスレッドから)外部でスレッドを終了できるようです。

それがオプションでない場合は、デバッグAPIを試すことができます。 lua_sethookを使用すると、フックでスレッドを正常に終了する方法があると想定して、制御を取り戻すことができます。

2
sylvanaar

おそらく役に立たないかもしれませんが、私が使用するlua(luaplayerまたはPGELua)では、

os.exit() 

または

pge.exit()
2
Richard Sparrow

スクリプト自体からの介入に頼らずに、長時間実行されている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
    
2
Mike M.

スクリプトを終了する方法は、errorを呼び出してエラーを発生させることです。ただし、ユーザーがpcallを介してスクリプトを呼び出した場合、このエラーはキャッチされます。

2
lhf

コルーチンを使用してスレッドを開始している場合は、coroutine.yield()を使用してスレッドを停止できます。

1
SDuke

https://github.com/amilamad/preemptive-task-scheduler-for-lua プロジェクトをご覧ください。 luaのプリエンプティブスケジューラー。フック内でlua_yeild関数を使用します。したがって、luaスレッドを一時停止できます。内部でもlongjmpを使用しますが、はるかに安全です。

1
amilamad

セッション:destroy();

Luaスクリプトを破棄したい場所でこの1行のコードを使用します。

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