Lua: ошибка в моей многозадачной утилите

У меня возникла проблема при создании класса для использования многозадачной системы в Lua. Этот класс должен позволять запускать несколько функций одновременно с использованием системы сопрограмм Lua.

Я хотел бы написать идеальное использование класса как в коде хоста, так и в коде Lua.

В ХОДЕ КОДА

int main(void)
{
    //Make state.
    auto L{luaL_newstate()};
    luaL_openlibs(L);

    //Enable multitasking.
    TaskSystem tasks{L};

    if (luaL_dofile(L, "script.lua") != 0)
    {
        auto msg{lua_tostring(L, -1)};
        std::cerr << msg << std::endl;
    }
    //Consider this as main loop of games.
    while (true)
    {
        tasks.resume_all();
        std::cin.get();
    }

    lua_close(L);
}

В коде LUA

--30 tasks run parallel.
--'run_task' creates coroutine from the function given,
-- and registers it to  the list of coroutines in the host code.
for i = 0, 30 do
    run_task
    (
    function()
        while true do
            print(i) 
            coroutine.yield()
        end
    end
    )
end

Я ожидаю, что пример, который я показываю, запускает функции, заданные 'run_task' в коде Lua, как будто они находятся в многозадачной системе.

Затем я показываю реализацию "класса TaskSystem". Извините, этот код немного длинный.

#include <list>
#include <boost/range/algorithm_ext/erase.hpp>
class TaskSystem
    {
    private:
        lua_State* main_thread;
        struct Task
        {
            lua_State* thread;
            ThreadStatus status;
            bool to_continue(void) const {return status == LUA_YIELD; }
        };
        using TaskList = std::list<Task>;
        TaskList task_list;
        static int run_task(lua_State* _parent)
        {
            //coroutine.create(func)
            lua_getglobal(_parent, "coroutine");
            lua_getfield(_parent, -1, "create");
            lua_pushvalue(_parent, 1); //argument
            lua_pcall(_parent, 1, 1, 0);

            //Converte coroutine into thread object.
            auto co{lua_tothread(_parent, -1)};

            lua_settop(_parent, 0);

            //Register the coroutine.
            auto& task_list{*static_cast<TaskList*>(lua_touserdata(_parent, lua_upvalueindex(1)))};
            task_list.push_back({co, lua_resume(co, nullptr, 0)});

            return 0;
        }
    public:
        TaskSystem(void) :main_thread(nullptr), task_list(){}
        //Enable multitasking.
        TaskSystem(lua_State* _main_thread)
        {
            initialize(_main_thread);
        }
        //Enable multitasiking in the thread.
        void initialize(lua_State* _main_thread)
        {
            if (!_main_thread)
            {
                throw std::runtime_error("TaskSystem must be initialized by valid thread.");
            }

            main_thread = _main_thread;
            lua_pushlightuserdata(main_thread, &task_list); //Task list as upvalue.
            lua_pushcclosure(main_thread, &run_task, 1);
            lua_setglobal(main_thread, "run_task");
        }
        //Resume all tasks.
        void resume_all(void)
        {
            //Remove all threads not resumable.
            //'boost::remove_erase_if' is the function splicing the container(1st arg) according to the lambda(2nd arg).
            boost::remove_erase_if(task_list, [](const Task& _t) {return !_t.to_continue(); });

            //Resume all thread on the list.
            for (auto& t : task_list)
            {
                if (t.to_continue())
                {
                    t.status = lua_resume(t.thread, nullptr, 0);
                }
            }
        }
        //Return the number of the tasks.
        std::size_t count(void) const { return task_list.size(); }
    };

Проблема, которая смущает меня, заключается в неизвестном исключении, выдаваемом TaskSystem::resume_all при попытке запустить этот код.

Исключение имеет тенденцию вызываться, когда число задач составляет 24 или больше.

Когда-то я думал, что stackru происходит в среде Lua, но возникает исключение, даже если я увеличиваю размер стека на lua_checkstack.

Если кто-то заметит решение, пожалуйста, помогите мне.

Моя среда

Lua версия: 5.2.4

IDE / компилятор: сообщество Microsoft Visual Studio 2015

Язык кода хоста: C++14

ПЕРЕСМОТРЕННЫЙ КОД

class TaskSystem
{
private:
    using ThreadRef = decltype(luaL_ref(nullptr, 0));
    lua_State* main_thread;
    struct Task
    {
        ThreadRef thread;
        ThreadStatus status;
        bool to_continue(void) const {return status == LUA_YIELD; }
    };
    using TaskList = std::list<Task>;
    TaskList task_list;
    static int run_task(lua_State* _parent)
    {
        //coroutine.create(func)
        lua_getglobal(_parent, "coroutine");
        lua_getfield(_parent, -1, "create");
        lua_pushvalue(_parent, 1); //argument
        lua_pcall(_parent, 1, 1, 0);

        //co is reference to the thread object.
        auto co{luaL_ref(_parent, LUA_REGISTRYINDEX)};

        //Register the coroutine.
        auto& task_list{*static_cast<TaskList*>(lua_touserdata(_parent, lua_upvalueindex(1)))};
        task_list.push_back({co, LUA_YIELD});

        return 0;
    }
public:
    TaskSystem(void) :main_thread(nullptr), task_list(){}
    //Enable multitasking.
    TaskSystem(lua_State* _main_thread)
    {
        initialize(_main_thread);
    }
    //Enable multitasiking in the thread.
    void initialize(lua_State* _main_thread)
    {
        if (!_main_thread)
        {
            throw std::runtime_error("TaskSystem must be initialized by valid thread.");
        }

        main_thread = _main_thread;
        lua_pushlightuserdata(main_thread, &task_list); //Task list as upvalue.
        lua_pushcclosure(main_thread, &run_task, 1);
        lua_setglobal(main_thread, "run_task");
    }
    //Resume all tasks.
    void resume_all(void)
    {
        //Remove all threads not resumable.
        boost::remove_erase_if(task_list, [](const Task& _t) {return !_t.to_continue(); });

        //Resume all thread on the list.
        for (auto& t : task_list)
        {
            //Get thread object,
            lua_rawgeti(main_thread, LUA_REGISTRYINDEX, t.thread);
            auto thread{ lua_tothread(main_thread, -1) };
            //and call it.
            t.status = lua_resume(thread, nullptr, 0);
            //Disable referenct to the thread if its life-time ended.
            if(!t.to_continue()){luaL_unref(main_thread, LUA_REGISTRYINDEX, t.thread);}
        }
    }
    //Return the number of the tasks.
    std::size_t count(void) const { return task_list.size(); }
}

0 ответов

Другие вопросы по тегам