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);
}
--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 или больше.

Однажды я подумал, что stackoverflow происходит в среде 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(); }
}

1

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]