spicetools/script/instance.cpp

134 lines
3.3 KiB
C++

#include "instance.h"
#include <lua.hpp>
#include "util/logging.h"
#include "lib.h"
namespace script {
thread_local Instance *INSTANCE;
struct LuaData {
lua_State *L = nullptr;
~LuaData() {
if (L) {
lua_close(L);
L = nullptr;
}
}
};
static void lua_hook(lua_State *L, lua_Debug *data) {
auto instance = INSTANCE;
do {
// check if running
if (!instance->is_running()) {
luaL_error(L, "_stop_");
return;
}
// check if paused
if (instance->is_paused()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
} while (instance->is_paused());
}
Instance::Instance(const char *str, bool is_path, bool sandbox) {
if (is_path) {
log_misc("script", "loading {}", str);
}
// create state
lua = new LuaData();
lua->L = luaL_newstate();
if (!lua->L) {
error = true;
log_warning("script", "unable to create state");
return;
}
// load libraries
load_libs(lua->L, sandbox);
// load script
int err;
if (is_path) {
err = luaL_loadfile(lua->L, str);
} else {
err = luaL_loadstring(lua->L, str);
}
// check for error
if (err != 0) {
error = true;
auto err_str = std::string(lua_tostring(lua->L, -1));
if (is_path) {
log_warning("script", "error loading '{}':\n{}", str, err_str);
} else {
log_warning("script", "error parsing:\n{}", err_str);
}
return;
}
// start script thread
running = true;
thread = new std::thread([this, str, is_path] {
// insert hooks
INSTANCE = this;
lua_sethook(lua->L, &lua_hook, LUA_MASKCOUNT, 1024);
lua_sethook(lua->L, &lua_hook, LUA_MASKCALL, 1);
lua_sethook(lua->L, &lua_hook, LUA_MASKRET, 1);
// call script
auto err = lua_pcall(lua->L, 0, LUA_MULTRET, 0);
// display error
if (err != 0) {
auto err_str = std::string(lua_tostring(lua->L, -1));
if (!err_str.ends_with("_stop_")) {
error = true;
if (is_path) {
log_warning("script", "runtime error in '{}':\n{}", str, err_str);
} else {
log_warning("script", "runtime error:\n{}", err_str);
}
}
}
// done
running = false;
});
}
Instance::~Instance() {
stop();
delete lua;
}
void Instance::join() {
if (thread) {
if (thread->joinable()) {
thread->join();
}
std::lock_guard<std::mutex> lock(thread_m);
delete thread;
thread = nullptr;
}
}
void Instance::stop() {
running = false;
join();
}
void Instance::pause() {
paused = true;
}
void Instance::resume() {
paused = false;
}
}