From 6ab3b4c83856b5c8a1a526c0e4dc55babe79d50d Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Tue, 15 Apr 2014 13:41:07 -0400 Subject: [PATCH] Remove dependency on marshal and many other async changes This makes a number of changes: * Remove the dependency on marshal by using string.dump and loadstring. * Use lua_tolstring rather than having Lua functions pass string lengths to C++. * Move lua_api/l_async_events.* to cpp_api/s_async.*, where it belongs. * Make AsyncWorkerThread a child of ScriptApiBase, this removes some duplicate functionality. * Don't wait for async threads to shut down. (Is this safe? Might result in corruption if the thread is writing to a file.) * Pop more unused items from the stack * Code style fixes * Other misc changes --- builtin/async_env.lua | 24 +- builtin/async_event.lua | 68 ++-- src/guiEngine.cpp | 50 +-- src/httpfetch.cpp | 2 +- src/script/cpp_api/CMakeLists.txt | 1 + src/script/cpp_api/s_async.cpp | 301 ++++++++++++++ src/script/cpp_api/s_async.h | 171 ++++++++ src/script/lua_api/CMakeLists.txt | 2 - src/script/lua_api/l_async_events.cpp | 370 ----------------- src/script/lua_api/l_async_events.h | 220 ---------- src/script/lua_api/l_mainmenu.cpp | 19 +- src/script/lua_api/l_util.cpp | 2 +- src/script/lua_api/marshall.c | 551 -------------------------- src/script/scripting_game.cpp | 9 +- src/script/scripting_mainmenu.cpp | 32 +- src/script/scripting_mainmenu.h | 11 +- 16 files changed, 571 insertions(+), 1262 deletions(-) create mode 100644 src/script/cpp_api/s_async.cpp create mode 100644 src/script/cpp_api/s_async.h delete mode 100644 src/script/lua_api/l_async_events.cpp delete mode 100644 src/script/lua_api/l_async_events.h delete mode 100644 src/script/lua_api/marshall.c diff --git a/builtin/async_env.lua b/builtin/async_env.lua index afc69219..cdcb82ee 100644 --- a/builtin/async_env.lua +++ b/builtin/async_env.lua @@ -1,19 +1,21 @@ -engine.log("info","Initializing Asynchronous environment") +engine.log("info", "Initializing Asynchronous environment") +local tbl = engine or minetest +minetest = tbl +dofile(SCRIPTDIR .. DIR_DELIM .. "serialize.lua") dofile(SCRIPTDIR .. DIR_DELIM .. "misc_helpers.lua") -function engine.job_processor(serialized_function, serialized_data) +function tbl.job_processor(serialized_func, serialized_param) + local func = loadstring(serialized_func) + local param = tbl.deserialize(serialized_param) + local retval = nil - local fct = marshal.decode(serialized_function) - local params = marshal.decode(serialized_data) - local retval = marshal.encode(nil) - - if fct ~= nil and type(fct) == "function" then - local result = fct(params) - retval = marshal.encode(result) + if type(func) == "function" then + retval = tbl.serialize(func(param)) else - engine.log("error","ASYNC WORKER: unable to deserialize function") + tbl.log("error", "ASYNC WORKER: Unable to deserialize function") end - return retval,retval:len() + return retval or tbl.serialize(nil) end + diff --git a/builtin/async_event.lua b/builtin/async_event.lua index f4c7d244..2c3fb8fa 100644 --- a/builtin/async_event.lua +++ b/builtin/async_event.lua @@ -1,59 +1,45 @@ local tbl = engine or minetest +local SCRIPTDIR = SCRIPTDIR or tbl.get_scriptdir() +minetest = tbl +dofile(SCRIPTDIR .. DIR_DELIM .. "serialize.lua") + tbl.async_jobs = {} -if engine ~= nil then - function tbl.async_event_handler(jobid, serialized_retval) - local retval = nil - if serialized_retval ~= "ERROR" then - retval= marshal.decode(serialized_retval) - else - tbl.log("error","Error fetching async result") - end - - assert(type(tbl.async_jobs[jobid]) == "function") - tbl.async_jobs[jobid](retval) - tbl.async_jobs[jobid] = nil - end -else - - minetest.register_globalstep( - function(dtime) - local list = tbl.get_finished_jobs() - - for i=1,#list,1 do - local retval = marshal.decode(list[i].retval) - - assert(type(tbl.async_jobs[jobid]) == "function") - tbl.async_jobs[list[i].jobid](retval) - tbl.async_jobs[list[i].jobid] = nil - end - end) +local function handle_job(jobid, serialized_retval) + local retval = tbl.deserialize(serialized_retval) + assert(type(tbl.async_jobs[jobid]) == "function") + tbl.async_jobs[jobid](retval) + tbl.async_jobs[jobid] = nil end -function tbl.handle_async(fct, parameters, callback) +if engine ~= nil then + tbl.async_event_handler = handle_job +else + minetest.register_globalstep(function(dtime) + for i, job in ipairs(tbl.get_finished_jobs()) do + handle_job(job.jobid, job.retval) + end + end) +end - --serialize fct - local serialized_fct = marshal.encode(fct) +function tbl.handle_async(func, parameter, callback) + -- Serialize function + local serialized_func = string.dump(func) - assert(marshal.decode(serialized_fct) ~= nil) + assert(serialized_func ~= nil) - --serialize parameters - local serialized_params = marshal.encode(parameters) + -- Serialize parameters + local serialized_param = tbl.serialize(parameter) - if serialized_fct == nil or - serialized_params == nil or - serialized_fct:len() == 0 or - serialized_params:len() == 0 then + if serialized_param == nil then return false end - local jobid = tbl.do_async_callback( serialized_fct, - serialized_fct:len(), - serialized_params, - serialized_params:len()) + local jobid = tbl.do_async_callback(serialized_func, serialized_param) tbl.async_jobs[jobid] = callback return true end + diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index b929c4f7..672a0d37 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -186,19 +186,20 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, // Initialize scripting - infostream<<"GUIEngine: Initializing Lua"<errormessage != "") - { + if (m_data->errormessage != "") { m_script->setMainMenuErrorMessage(m_data->errormessage); m_data->errormessage = ""; } - if (!loadMainMenuScript()) - assert("no future without mainmenu" == 0); + if (!loadMainMenuScript()) { + errorstream << "No future without mainmenu" << std::endl; + abort(); + } run(); } @@ -512,32 +513,25 @@ bool GUIEngine::setTexture(texture_layer layer,std::string texturepath) { /******************************************************************************/ bool GUIEngine::downloadFile(std::string url,std::string target) { #if USE_CURL - bool retval = true; + std::ofstream targetfile(target.c_str(), std::ios::out | std::ios::binary); - FILE* targetfile = fopen(target.c_str(),"wb"); - - if (targetfile) { - HTTPFetchRequest fetchrequest; - HTTPFetchResult fetchresult; - fetchrequest.url = url; - fetchrequest.caller = HTTPFETCH_SYNC; - httpfetch_sync(fetchrequest,fetchresult); - - if (fetchresult.succeeded) { - if (fwrite(fetchresult.data.c_str(),1,fetchresult.data.size(),targetfile) != fetchresult.data.size()) { - retval = false; - } - } - else { - retval = false; - } - fclose(targetfile); - } - else { - retval = false; + if (!targetfile.good()) { + return false; } - return retval; + HTTPFetchRequest fetchrequest; + HTTPFetchResult fetchresult; + fetchrequest.url = url; + fetchrequest.caller = HTTPFETCH_SYNC; + httpfetch_sync(fetchrequest, fetchresult); + + if (fetchresult.succeeded) { + targetfile << fetchresult.data; + } else { + return false; + } + + return true; #else return false; #endif diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index 751a4471..313988fd 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -45,7 +45,7 @@ HTTPFetchRequest::HTTPFetchRequest() caller = HTTPFETCH_DISCARD; request_id = 0; timeout = g_settings->getS32("curl_timeout"); - connect_timeout = timeout * 5; + connect_timeout = timeout; useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")"; } diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt index b753eda1..c4502005 100644 --- a/src/script/cpp_api/CMakeLists.txt +++ b/src/script/cpp_api/CMakeLists.txt @@ -9,6 +9,7 @@ set(common_SCRIPT_CPP_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/s_nodemeta.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_player.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_server.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp PARENT_SCOPE) # Used by client only diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp new file mode 100644 index 00000000..603b6fe9 --- /dev/null +++ b/src/script/cpp_api/s_async.cpp @@ -0,0 +1,301 @@ +/* +Minetest +Copyright (C) 2013 sapier, + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +} + +#include "s_async.h" +#include "log.h" +#include "filesys.h" +#include "porting.h" +#include "common/c_internal.h" + +/******************************************************************************/ +AsyncEngine::AsyncEngine() : + m_initDone(false), + m_JobIdCounter(0) +{ +} + +/******************************************************************************/ +AsyncEngine::~AsyncEngine() +{ + // Force kill all threads + for (std::vector::iterator i = m_WorkerThreads.begin(); + i != m_WorkerThreads.end(); i++) { + (*i)->Kill(); + delete *i; + } + + m_JobQueueMutex.Lock(); + m_JobQueue.clear(); + m_JobQueueMutex.Unlock(); + m_WorkerThreads.clear(); +} + +/******************************************************************************/ +bool AsyncEngine::registerFunction(const char* name, lua_CFunction func) +{ + if (m_initDone) { + return false; + } + m_FunctionList[name] = func; + return true; +} + +/******************************************************************************/ +void AsyncEngine::Initialize(unsigned int numEngines) +{ + m_initDone = true; + + for (unsigned int i = 0; i < numEngines; i++) { + AsyncWorkerThread* toAdd = new AsyncWorkerThread(this, i); + m_WorkerThreads.push_back(toAdd); + toAdd->Start(); + } +} + +/******************************************************************************/ +unsigned int AsyncEngine::doAsyncJob(std::string func, std::string params) +{ + m_JobQueueMutex.Lock(); + LuaJobInfo toadd; + toadd.JobId = m_JobIdCounter++; + toadd.serializedFunction = func; + toadd.serializedParams = params; + + m_JobQueue.push_back(toadd); + + m_JobQueueCounter.Post(); + + m_JobQueueMutex.Unlock(); + + return toadd.JobId; +} + +/******************************************************************************/ +LuaJobInfo AsyncEngine::getJob() +{ + m_JobQueueCounter.Wait(); + m_JobQueueMutex.Lock(); + + LuaJobInfo retval; + retval.valid = false; + + if (m_JobQueue.size() != 0) { + retval = m_JobQueue.front(); + retval.valid = true; + m_JobQueue.erase(m_JobQueue.begin()); + } + m_JobQueueMutex.Unlock(); + + return retval; +} + +/******************************************************************************/ +void AsyncEngine::putJobResult(LuaJobInfo result) +{ + m_ResultQueueMutex.Lock(); + m_ResultQueue.push_back(result); + m_ResultQueueMutex.Unlock(); +} + +/******************************************************************************/ +void AsyncEngine::Step(lua_State *L, int errorhandler) +{ + lua_getglobal(L, "engine"); + m_ResultQueueMutex.Lock(); + while (!m_ResultQueue.empty()) { + LuaJobInfo jobdone = m_ResultQueue.front(); + m_ResultQueue.erase(m_ResultQueue.begin()); + + lua_getfield(L, -1, "async_event_handler"); + + if (lua_isnil(L, -1)) { + assert("Async event handler does not exist!" == 0); + } + + luaL_checktype(L, -1, LUA_TFUNCTION); + + lua_pushinteger(L, jobdone.JobId); + lua_pushlstring(L, jobdone.serializedResult.c_str(), + jobdone.serializedResult.length()); + + if (lua_pcall(L, 2, 0, errorhandler)) { + script_error(L); + } + } + m_ResultQueueMutex.Unlock(); + lua_pop(L, 1); // Pop engine +} + +/******************************************************************************/ +void AsyncEngine::PushFinishedJobs(lua_State* L) { + // Result Table + m_ResultQueueMutex.Lock(); + + unsigned int index = 1; + lua_createtable(L, m_ResultQueue.size(), 0); + int top = lua_gettop(L); + + while (!m_ResultQueue.empty()) { + LuaJobInfo jobdone = m_ResultQueue.front(); + m_ResultQueue.erase(m_ResultQueue.begin()); + + lua_createtable(L, 0, 2); // Pre-alocate space for two map fields + int top_lvl2 = lua_gettop(L); + + lua_pushstring(L, "jobid"); + lua_pushnumber(L, jobdone.JobId); + lua_settable(L, top_lvl2); + + lua_pushstring(L, "retval"); + lua_pushlstring(L, jobdone.serializedResult.data(), + jobdone.serializedResult.size()); + lua_settable(L, top_lvl2); + + lua_rawseti(L, top, index++); + } + + m_ResultQueueMutex.Unlock(); +} + +/******************************************************************************/ +void AsyncEngine::PrepareEnvironment(lua_State* L, int top) { + for (std::map::iterator it = m_FunctionList.begin(); + it != m_FunctionList.end(); it++) { + lua_pushstring(L, it->first.c_str()); + lua_pushcfunction(L, it->second); + lua_settable(L, top); + } +} + +/******************************************************************************/ +AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher, + unsigned int threadNum) : + ScriptApiBase(), + m_JobDispatcher(jobDispatcher), + m_threadnum(threadNum) +{ + lua_State *L = getStack(); + + luaL_openlibs(L); + + // Prepare job lua environment + lua_newtable(L); + lua_setglobal(L, "engine"); + lua_getglobal(L, "engine"); + int top = lua_gettop(L); + + lua_pushstring(L, DIR_DELIM); + lua_setglobal(L, "DIR_DELIM"); + + lua_pushstring(L, + (porting::path_share + DIR_DELIM + "builtin").c_str()); + lua_setglobal(L, "SCRIPTDIR"); + + m_JobDispatcher->PrepareEnvironment(L, top); +} + +/******************************************************************************/ +AsyncWorkerThread::~AsyncWorkerThread() +{ + assert(IsRunning() == false); +} + +/******************************************************************************/ +void* AsyncWorkerThread::Thread() +{ + ThreadStarted(); + + // Register thread for error logging + char number[21]; + snprintf(number, sizeof(number), "%d", m_threadnum); + log_register_thread(std::string("AsyncWorkerThread_") + number); + + porting::setThreadName((std::string("AsyncWorkTh_") + number).c_str()); + + std::string asyncscript = porting::path_share + DIR_DELIM + "builtin" + + DIR_DELIM + "async_env.lua"; + + if (!loadScript(asyncscript)) { + errorstream + << "AsyncWorkderThread execution of async base environment failed!" + << std::endl; + abort(); + } + + lua_State *L = getStack(); + // Main loop + while (!StopRequested()) { + // Wait for job + LuaJobInfo toProcess = m_JobDispatcher->getJob(); + + if (toProcess.valid == false || StopRequested()) { + continue; + } + + lua_getglobal(L, "engine"); + if (lua_isnil(L, -1)) { + errorstream << "Unable to find engine within async environment!"; + abort(); + } + + lua_getfield(L, -1, "job_processor"); + if (lua_isnil(L, -1)) { + errorstream << "Unable to get async job processor!" << std::endl; + abort(); + } + + luaL_checktype(L, -1, LUA_TFUNCTION); + + // Call it + lua_pushlstring(L, + toProcess.serializedFunction.data(), + toProcess.serializedFunction.size()); + lua_pushlstring(L, + toProcess.serializedParams.data(), + toProcess.serializedParams.size()); + + if (lua_pcall(L, 2, 1, m_errorhandler)) { + scriptError(); + toProcess.serializedResult = ""; + } else { + // Fetch result + size_t length; + const char *retval = lua_tolstring(L, -1, &length); + toProcess.serializedResult = std::string(retval, length); + } + + // Pop engine, job_processor, and retval + lua_pop(L, 3); + + // Put job result + m_JobDispatcher->putJobResult(toProcess); + } + log_deregister_thread(); + return 0; +} + diff --git a/src/script/cpp_api/s_async.h b/src/script/cpp_api/s_async.h new file mode 100644 index 00000000..c5c0e091 --- /dev/null +++ b/src/script/cpp_api/s_async.h @@ -0,0 +1,171 @@ +/* +Minetest +Copyright (C) 2013 sapier, + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef L_ASYNC_EVENTS_H_ +#define L_ASYNC_EVENTS_H_ + +#include +#include + +#include "jthread/jthread.h" +#include "jthread/jmutex.h" +#include "jthread/jsemaphore.h" +#include "debug.h" +#include "lua.h" +#include "cpp_api/s_base.h" + +// Forward declarations +class AsyncEngine; + + +// Declarations + +// Data required to queue a job +struct LuaJobInfo { + // Function to be called in async environment + std::string serializedFunction; + // Parameter to be passed to function + std::string serializedParams; + // Result of function call + std::string serializedResult; + // JobID used to identify a job and match it to callback + unsigned int JobId; + + bool valid; +}; + +// Asynchronous working environment +class AsyncWorkerThread : public JThread, public ScriptApiBase { +public: + /** + * default constructor + * @param pointer to job dispatcher + */ + AsyncWorkerThread(AsyncEngine* jobDispatcher, unsigned int threadNum); + + virtual ~AsyncWorkerThread(); + + void* Thread(); + +private: + AsyncEngine* m_JobDispatcher; + + // Thread number. Used for debug output + unsigned int m_threadnum; + +}; + +// Asynchornous thread and job management +class AsyncEngine { + friend class AsyncWorkerThread; +public: + AsyncEngine(); + ~AsyncEngine(); + + /** + * Register function to be used within engine + * @param name Function name to be used within Lua environment + * @param func C function to be called + */ + bool registerFunction(const char* name, lua_CFunction func); + + /** + * Create async engine tasks and lock function registration + * @param numEngines Number of async threads to be started + */ + void Initialize(unsigned int numEngines); + + /** + * queue/run a async job + * @param func Serialized lua function + * @param params Serialized parameters + * @return jobid The job is queued + */ + unsigned int doAsyncJob(std::string func, std::string params); + + /** + * Engine step to process finished jobs + * the engine step is one way to pass events back, PushFinishedJobs another + * @param L The Lua stack + * @param errorhandler Stack index of the Lua error handler + */ + void Step(lua_State *L, int errorhandler); + + /** + * Push a list of finished jobs onto the stack + * @param L The Lua stack + */ + void PushFinishedJobs(lua_State *L); + +protected: + /** + * Get a Job from queue to be processed + * this function blocks until a job is ready + * @return a job to be processed + */ + LuaJobInfo getJob(); + + /** + * Put a Job result back to result queue + * @param result result of completed job + */ + void putJobResult(LuaJobInfo result); + + /** + * Initialize environment with current registred functions + * this function adds all functions registred by registerFunction to the + * passed lua stack + * @param L Lua stack to initialize + * @param top Stack position + */ + void PrepareEnvironment(lua_State* L, int top); + +private: + + // Stack index of error handler + int m_errorhandler; + + // variable locking the engine against further modification + bool m_initDone; + + // Internal store for registred functions + std::map m_FunctionList; + + // Internal counter to create job IDs + unsigned int m_JobIdCounter; + + // Mutex to protect job queue + JMutex m_JobQueueMutex; + + // Job queue + std::vector m_JobQueue; + + // Mutex to protect result queue + JMutex m_ResultQueueMutex; + // Result queue + std::vector m_ResultQueue; + + // List of current worker threads + std::vector m_WorkerThreads; + + // Counter semaphore for job dispatching + JSemaphore m_JobQueueCounter; +}; + +#endif // L_ASYNC_EVENTS_H_ diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index 0b89df6a..08960d2a 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -16,8 +16,6 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/l_async_events.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/marshall.c PARENT_SCOPE) # Used by client only diff --git a/src/script/lua_api/l_async_events.cpp b/src/script/lua_api/l_async_events.cpp deleted file mode 100644 index f5c27a23..00000000 --- a/src/script/lua_api/l_async_events.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* -Minetest -Copyright (C) 2013 sapier, - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -extern "C" { -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" -int luaopen_marshal(lua_State *L); -} -#include - -#include "l_async_events.h" -#include "log.h" -#include "filesys.h" -#include "porting.h" -#include "common/c_internal.h" - -/******************************************************************************/ -AsyncEngine::AsyncEngine() : - m_initDone(false), - m_JobIdCounter(0) -{ -} - -/******************************************************************************/ -AsyncEngine::~AsyncEngine() -{ - /** request all threads to stop **/ - for (std::vector::iterator i= m_WorkerThreads.begin(); - i != m_WorkerThreads.end();i++) { - (*i)->Stop(); - } - - - /** wakeup all threads **/ - for (std::vector::iterator i= m_WorkerThreads.begin(); - i != m_WorkerThreads.end();i++) { - m_JobQueueCounter.Post(); - } - - /** wait for threads to finish **/ - for (std::vector::iterator i= m_WorkerThreads.begin(); - i != m_WorkerThreads.end();i++) { - (*i)->Wait(); - } - - /** force kill all threads **/ - for (std::vector::iterator i= m_WorkerThreads.begin(); - i != m_WorkerThreads.end();i++) { - (*i)->Kill(); - delete *i; - } - - m_JobQueueMutex.Lock(); - m_JobQueue.clear(); - m_JobQueueMutex.Unlock(); - m_WorkerThreads.clear(); -} - -/******************************************************************************/ -bool AsyncEngine::registerFunction(const char* name, lua_CFunction fct) { - - if (m_initDone) return false; - m_FunctionList[name] = fct; - return true; -} - -/******************************************************************************/ -void AsyncEngine::Initialize(unsigned int numengines) { - m_initDone = true; - - for (unsigned int i=0; i < numengines; i ++) { - - AsyncWorkerThread* toadd = new AsyncWorkerThread(this,i); - m_WorkerThreads.push_back(toadd); - toadd->Start(); - } -} - -/******************************************************************************/ -unsigned int AsyncEngine::doAsyncJob(std::string fct, std::string params) { - - m_JobQueueMutex.Lock(); - LuaJobInfo toadd; - toadd.JobId = m_JobIdCounter++; - toadd.serializedFunction = fct; - toadd.serializedParams = params; - - m_JobQueue.push_back(toadd); - - m_JobQueueCounter.Post(); - - m_JobQueueMutex.Unlock(); - - return toadd.JobId; -} - -/******************************************************************************/ -LuaJobInfo AsyncEngine::getJob() { - - m_JobQueueCounter.Wait(); - m_JobQueueMutex.Lock(); - - LuaJobInfo retval; - retval.valid = false; - - if (m_JobQueue.size() != 0) { - retval = m_JobQueue.front(); - retval.valid = true; - m_JobQueue.erase((m_JobQueue.begin())); - } - m_JobQueueMutex.Unlock(); - - return retval; -} - -/******************************************************************************/ -void AsyncEngine::putJobResult(LuaJobInfo result) { - m_ResultQueueMutex.Lock(); - m_ResultQueue.push_back(result); - m_ResultQueueMutex.Unlock(); -} - -/******************************************************************************/ -void AsyncEngine::Step(lua_State *L) { - lua_pushcfunction(L, script_error_handler); - int errorhandler = lua_gettop(L); - lua_getglobal(L, "engine"); - m_ResultQueueMutex.Lock(); - while(!m_ResultQueue.empty()) { - LuaJobInfo jobdone = m_ResultQueue.front(); - m_ResultQueue.erase(m_ResultQueue.begin()); - - lua_getfield(L, -1, "async_event_handler"); - - if(lua_isnil(L, -1)) - assert("Someone managed to destroy a async callback in engine!" == 0); - - luaL_checktype(L, -1, LUA_TFUNCTION); - - lua_pushinteger(L, jobdone.JobId); - lua_pushlstring(L, jobdone.serializedResult.c_str(), - jobdone.serializedResult.length()); - - if(lua_pcall(L, 2, 0, errorhandler)) { - script_error(L); - } - } - m_ResultQueueMutex.Unlock(); - lua_pop(L, 2); // Pop engine and error handler -} - -/******************************************************************************/ -void AsyncEngine::PushFinishedJobs(lua_State* L) { - //Result Table - m_ResultQueueMutex.Lock(); - - unsigned int index=1; - lua_newtable(L); - int top = lua_gettop(L); - - while(!m_ResultQueue.empty()) { - - LuaJobInfo jobdone = m_ResultQueue.front(); - m_ResultQueue.erase(m_ResultQueue.begin()); - - lua_pushnumber(L,index); - - lua_newtable(L); - int top_lvl2 = lua_gettop(L); - - lua_pushstring(L,"jobid"); - lua_pushnumber(L,jobdone.JobId); - lua_settable(L, top_lvl2); - - lua_pushstring(L,"retval"); - lua_pushstring(L, jobdone.serializedResult.c_str()); - lua_settable(L, top_lvl2); - - lua_settable(L, top); - index++; - } - - m_ResultQueueMutex.Unlock(); - -} -/******************************************************************************/ -void AsyncEngine::PrepareEnvironment(lua_State* L, int top) { - for(std::map::iterator i = m_FunctionList.begin(); - i != m_FunctionList.end(); i++) { - - lua_pushstring(L,i->first.c_str()); - lua_pushcfunction(L,i->second); - lua_settable(L, top); - - } -} - -/******************************************************************************/ -int async_worker_ErrorHandler(lua_State *L) { - lua_getfield(L, LUA_GLOBALSINDEX, "debug"); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - return 1; - } - lua_getfield(L, -1, "traceback"); - if (!lua_isfunction(L, -1)) { - lua_pop(L, 2); - return 1; - } - lua_pushvalue(L, 1); - lua_pushinteger(L, 2); - lua_call(L, 2, 1); - return 1; -} - -/******************************************************************************/ -AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobdispatcher, - unsigned int numthreadnumber) : - m_JobDispatcher(jobdispatcher), - m_luaerrorhandler(-1), - m_threadnum(numthreadnumber) -{ - // create luastack - m_LuaStack = luaL_newstate(); - - // load basic lua modules - luaL_openlibs(m_LuaStack); - - // load serialization functions - luaopen_marshal(m_LuaStack); -} - -/******************************************************************************/ -AsyncWorkerThread::~AsyncWorkerThread() { - - assert(IsRunning() == false); - lua_close(m_LuaStack); -} - -/******************************************************************************/ -void* AsyncWorkerThread::worker_thread_main() { - - //register thread for error logging - char number[21]; - snprintf(number,sizeof(number),"%d",m_threadnum); - log_register_thread(std::string("AsyncWorkerThread_") + number); - - porting::setThreadName( - std::string(std::string("AsyncWorkTh_") + number).c_str()); - - /** prepare job lua environment **/ - lua_newtable(m_LuaStack); - lua_setglobal(m_LuaStack, "engine"); - - lua_getglobal(m_LuaStack, "engine"); - int top = lua_gettop(m_LuaStack); - - lua_pushstring(m_LuaStack, DIR_DELIM); - lua_setglobal(m_LuaStack, "DIR_DELIM"); - - lua_pushstring(m_LuaStack, - std::string(porting::path_share + DIR_DELIM + "builtin").c_str()); - lua_setglobal(m_LuaStack, "SCRIPTDIR"); - - - m_JobDispatcher->PrepareEnvironment(m_LuaStack,top); - - std::string asyncscript = - porting::path_share + DIR_DELIM + "builtin" - + DIR_DELIM + "async_env.lua"; - - lua_pushcfunction(m_LuaStack, async_worker_ErrorHandler); - m_luaerrorhandler = lua_gettop(m_LuaStack); - - if(!runScript(asyncscript)) { - infostream - << "AsyncWorkderThread::worker_thread_main execution of async base environment failed!" - << std::endl; - assert("no future with broken builtin async environment scripts" == 0); - } - /** main loop **/ - while(!StopRequested()) { - //wait for job - LuaJobInfo toprocess = m_JobDispatcher->getJob(); - - if (toprocess.valid == false) { continue; } - if (StopRequested()) { continue; } - - //first push error handler - lua_pushcfunction(m_LuaStack, script_error_handler); - int errorhandler = lua_gettop(m_LuaStack); - - lua_getglobal(m_LuaStack, "engine"); - if(lua_isnil(m_LuaStack, -1)) - assert("unable to find engine within async environment" == 0); - - lua_getfield(m_LuaStack, -1, "job_processor"); - if(lua_isnil(m_LuaStack, -1)) - assert("Someone managed to destroy a async worker engine!" == 0); - - luaL_checktype(m_LuaStack, -1, LUA_TFUNCTION); - - //call it - lua_pushlstring(m_LuaStack, - toprocess.serializedFunction.c_str(), - toprocess.serializedFunction.length()); - lua_pushlstring(m_LuaStack, - toprocess.serializedParams.c_str(), - toprocess.serializedParams.length()); - - if (StopRequested()) { continue; } - if(lua_pcall(m_LuaStack, 2, 2, errorhandler)) { - script_error(m_LuaStack); - toprocess.serializedResult = "ERROR"; - } else { - //fetch result - const char *retval = lua_tostring(m_LuaStack, -2); - unsigned int lenght = lua_tointeger(m_LuaStack,-1); - toprocess.serializedResult = std::string(retval,lenght); - } - - if (StopRequested()) { continue; } - //put job result - m_JobDispatcher->putJobResult(toprocess); - } - log_deregister_thread(); - return 0; -} - -/******************************************************************************/ -bool AsyncWorkerThread::runScript(std::string script) { - - int ret = luaL_loadfile(m_LuaStack, script.c_str()) || - lua_pcall(m_LuaStack, 0, 0, m_luaerrorhandler); - if(ret){ - errorstream<<"==== ERROR FROM LUA WHILE INITIALIZING ASYNC ENVIRONMENT ====="<worker_thread_main(); -} diff --git a/src/script/lua_api/l_async_events.h b/src/script/lua_api/l_async_events.h deleted file mode 100644 index 9d42b07c..00000000 --- a/src/script/lua_api/l_async_events.h +++ /dev/null @@ -1,220 +0,0 @@ -/* -Minetest -Copyright (C) 2013 sapier, - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef L_ASYNC_EVENTS_H_ -#define L_ASYNC_EVENTS_H_ - -#include -#include - -/******************************************************************************/ -/* Includes */ -/******************************************************************************/ -#include "jthread/jthread.h" -#include "jthread/jmutex.h" -#include "jthread/jsemaphore.h" -#include "debug.h" -#include "lua.h" - -/******************************************************************************/ -/* Typedefs and macros */ -/******************************************************************************/ -#define MAINMENU_NUMBER_OF_ASYNC_THREADS 4 - -/******************************************************************************/ -/* forward declarations */ -/******************************************************************************/ -class AsyncEngine; - -/******************************************************************************/ -/* declarations */ -/******************************************************************************/ - -/** a struct to encapsulate data required to queue a job **/ -struct LuaJobInfo { - /** function to be called in async environment **/ - std::string serializedFunction; - /** parameter table to be passed to function **/ - std::string serializedParams; - /** result of function call **/ - std::string serializedResult; - /** jobid used to identify a job and match it to callback **/ - unsigned int JobId; - /** valid marker **/ - bool valid; -}; - -/** class encapsulating a asynchronous working environment **/ -class AsyncWorkerThread : public JThread { -public: - /** - * default constructor - * @param pointer to job dispatcher - */ - AsyncWorkerThread(AsyncEngine* jobdispatcher, unsigned int threadnumber); - - /** - * default destructor - */ - virtual ~AsyncWorkerThread(); - - /** - * thread function - */ - void* Thread() { - ThreadStarted(); - return worker_thread_wrapper(this); - } - -private: - /** - * helper function to run a lua script - * @param path of script - */ - bool runScript(std::string script); - - /** - * main function of thread - */ - void* worker_thread_main(); - - /** - * static wrapper for thread creation - * @param this pointer to the thread to be created - */ - static void* worker_thread_wrapper(void* thread); - - /** - * pointer to job dispatcher - */ - AsyncEngine* m_JobDispatcher; - - /** - * the lua stack to run at - */ - lua_State* m_LuaStack; - - /** - * lua internal stack number of error handler - */ - int m_luaerrorhandler; - - /** - * thread number used for debug output - */ - unsigned int m_threadnum; - -}; - -/** asynchornous thread and job management **/ -class AsyncEngine { - friend class AsyncWorkerThread; -public: - /** - * default constructor - */ - AsyncEngine(); - /** - * default destructor - */ - ~AsyncEngine(); - - /** - * register function to be used within engines - * @param name function name to be used within lua environment - * @param fct c-function to be called - */ - bool registerFunction(const char* name, lua_CFunction fct); - - /** - * create async engine tasks and lock function registration - * @param numengines number of async threads to be started - */ - void Initialize(unsigned int numengines); - - /** - * queue/run a async job - * @param fct serialized lua function - * @param params serialized parameters - * @return jobid the job is queued - */ - unsigned int doAsyncJob(std::string fct, std::string params); - - /** - * engine step to process finished jobs - * the engine step is one way to pass events back, PushFinishedJobs another - * @param L the lua environment to do the step in - */ - void Step(lua_State *L); - - - void PushFinishedJobs(lua_State* L); - -protected: - /** - * Get a Job from queue to be processed - * this function blocks until a job is ready - * @return a job to be processed - */ - LuaJobInfo getJob(); - - /** - * put a Job result back to result queue - * @param result result of completed job - */ - void putJobResult(LuaJobInfo result); - - /** - * initialize environment with current registred functions - * this function adds all functions registred by registerFunction to the - * passed lua stack - * @param L lua stack to initialize - * @param top stack position - */ - void PrepareEnvironment(lua_State* L, int top); - -private: - - /** variable locking the engine against further modification **/ - bool m_initDone; - - /** internal store for registred functions **/ - std::map m_FunctionList; - - /** internal counter to create job id's **/ - unsigned int m_JobIdCounter; - - /** mutex to protect job queue **/ - JMutex m_JobQueueMutex; - /** job queue **/ - std::vector m_JobQueue; - - /** mutext to protect result queue **/ - JMutex m_ResultQueueMutex; - /** result queue **/ - std::vector m_ResultQueue; - - /** list of current worker threads **/ - std::vector m_WorkerThreads; - - /** counter semaphore for job dispatching **/ - JSemaphore m_JobQueueCounter; -}; - -#endif /* L_ASYNC_EVENTS_H_ */ diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 5de1c77f..31fc45ce 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_mainmenu.h" #include "lua_api/l_internal.h" #include "common/c_content.h" -#include "lua_api/l_async_events.h" +#include "cpp_api/s_async.h" #include "guiEngine.h" #include "guiMainMenu.h" #include "guiKeyChangeMenu.h" @@ -1034,19 +1034,18 @@ int ModApiMainMenu::l_do_async_callback(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - const char* serialized_fct_raw = luaL_checkstring(L, 1); - unsigned int lenght_fct = luaL_checkint(L, 2); + size_t func_length, param_length; + const char* serialized_func_raw = luaL_checklstring(L, 1, &func_length); - const char* serialized_params_raw = luaL_checkstring(L, 3); - unsigned int lenght_params = luaL_checkint(L, 4); + const char* serialized_param_raw = luaL_checklstring(L, 2, ¶m_length); - assert(serialized_fct_raw != 0); - assert(serialized_params_raw != 0); + assert(serialized_func_raw != NULL); + assert(serialized_param_raw != NULL); - std::string serialized_fct = std::string(serialized_fct_raw,lenght_fct); - std::string serialized_params = std::string(serialized_params_raw,lenght_params); + std::string serialized_func = std::string(serialized_func_raw, func_length); + std::string serialized_param = std::string(serialized_param_raw, param_length); - lua_pushinteger(L,engine->DoAsync(serialized_fct,serialized_params)); + lua_pushinteger(L, engine->DoAsync(serialized_func, serialized_param)); return 1; } diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index f2bbf181..90a1d77a 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "common/c_content.h" -#include "lua_api/l_async_events.h" +#include "cpp_api/s_async.h" #include "debug.h" #include "log.h" #include "tool.h" diff --git a/src/script/lua_api/marshall.c b/src/script/lua_api/marshall.c deleted file mode 100644 index ef70566c..00000000 --- a/src/script/lua_api/marshall.c +++ /dev/null @@ -1,551 +0,0 @@ -/* -* lmarshal.c -* A Lua library for serializing and deserializing Lua values -* Richard Hundt -* -* License: MIT -* -* Copyright (c) 2010 Richard Hundt -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include -#include - -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" - - -#define MAR_TREF 1 -#define MAR_TVAL 2 -#define MAR_TUSR 3 - -#define MAR_CHR 1 -#define MAR_I32 4 -#define MAR_I64 8 - -#define MAR_MAGIC 0x8e -#define SEEN_IDX 3 - -typedef struct mar_Buffer { - size_t size; - size_t seek; - size_t head; - char* data; -} mar_Buffer; - -static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx); -static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx); - -static void buf_init(lua_State *L, mar_Buffer *buf) -{ - buf->size = 128; - buf->seek = 0; - buf->head = 0; - if (!(buf->data = malloc(buf->size))) luaL_error(L, "Out of memory!"); -} - -static void buf_done(lua_State* L, mar_Buffer *buf) -{ - free(buf->data); -} - -static int buf_write(lua_State* L, const char* str, size_t len, mar_Buffer *buf) -{ - if (len > UINT32_MAX) luaL_error(L, "buffer too long"); - if (buf->size - buf->head < len) { - size_t new_size = buf->size << 1; - size_t cur_head = buf->head; - while (new_size - cur_head <= len) { - new_size = new_size << 1; - } - if (!(buf->data = realloc(buf->data, new_size))) { - luaL_error(L, "Out of memory!"); - } - buf->size = new_size; - } - memcpy(&buf->data[buf->head], str, len); - buf->head += len; - return 0; -} - -static const char* buf_read(lua_State *L, mar_Buffer *buf, size_t *len) -{ - if (buf->seek < buf->head) { - buf->seek = buf->head; - *len = buf->seek; - return buf->data; - } - *len = 0; - return NULL; -} - -static void mar_encode_value(lua_State *L, mar_Buffer *buf, int val, size_t *idx) -{ - size_t l; - int val_type = lua_type(L, val); - lua_pushvalue(L, val); - - buf_write(L, (void*)&val_type, MAR_CHR, buf); - switch (val_type) { - case LUA_TBOOLEAN: { - int int_val = lua_toboolean(L, -1); - buf_write(L, (void*)&int_val, MAR_CHR, buf); - break; - } - case LUA_TSTRING: { - const char *str_val = lua_tolstring(L, -1, &l); - buf_write(L, (void*)&l, MAR_I32, buf); - buf_write(L, str_val, l, buf); - break; - } - case LUA_TNUMBER: { - lua_Number num_val = lua_tonumber(L, -1); - buf_write(L, (void*)&num_val, MAR_I64, buf); - break; - } - case LUA_TTABLE: { - int tag, ref; - lua_pushvalue(L, -1); - lua_rawget(L, SEEN_IDX); - if (!lua_isnil(L, -1)) { - ref = lua_tointeger(L, -1); - tag = MAR_TREF; - buf_write(L, (void*)&tag, MAR_CHR, buf); - buf_write(L, (void*)&ref, MAR_I32, buf); - lua_pop(L, 1); - } - else { - mar_Buffer rec_buf; - lua_pop(L, 1); /* pop nil */ - if (luaL_getmetafield(L, -1, "__persist")) { - tag = MAR_TUSR; - - lua_pushvalue(L, -2); /* self */ - lua_call(L, 1, 1); - if (!lua_isfunction(L, -1)) { - luaL_error(L, "__persist must return a function"); - } - - lua_remove(L, -2); /* __persist */ - - lua_newtable(L); - lua_pushvalue(L, -2); /* callback */ - lua_rawseti(L, -2, 1); - - buf_init(L, &rec_buf); - mar_encode_table(L, &rec_buf, idx); - - buf_write(L, (void*)&tag, MAR_CHR, buf); - buf_write(L, (void*)&rec_buf.head, MAR_I32, buf); - buf_write(L, rec_buf.data, rec_buf.head, buf); - buf_done(L, &rec_buf); - lua_pop(L, 1); - } - else { - tag = MAR_TVAL; - - lua_pushvalue(L, -1); - lua_pushinteger(L, (*idx)++); - lua_rawset(L, SEEN_IDX); - - lua_pushvalue(L, -1); - buf_init(L, &rec_buf); - mar_encode_table(L, &rec_buf, idx); - lua_pop(L, 1); - - buf_write(L, (void*)&tag, MAR_CHR, buf); - buf_write(L, (void*)&rec_buf.head, MAR_I32, buf); - buf_write(L, rec_buf.data,rec_buf.head, buf); - buf_done(L, &rec_buf); - } - } - break; - } - case LUA_TFUNCTION: { - int tag, ref; - lua_pushvalue(L, -1); - lua_rawget(L, SEEN_IDX); - if (!lua_isnil(L, -1)) { - ref = lua_tointeger(L, -1); - tag = MAR_TREF; - buf_write(L, (void*)&tag, MAR_CHR, buf); - buf_write(L, (void*)&ref, MAR_I32, buf); - lua_pop(L, 1); - } - else { - mar_Buffer rec_buf; - int i; - lua_Debug ar; - lua_pop(L, 1); /* pop nil */ - - lua_pushvalue(L, -1); - lua_getinfo(L, ">nuS", &ar); - if (ar.what[0] != 'L') { - luaL_error(L, "attempt to persist a C function '%s'", ar.name); - } - tag = MAR_TVAL; - lua_pushvalue(L, -1); - lua_pushinteger(L, (*idx)++); - lua_rawset(L, SEEN_IDX); - - lua_pushvalue(L, -1); - buf_init(L, &rec_buf); - lua_dump(L, (lua_Writer)buf_write, &rec_buf); - - buf_write(L, (void*)&tag, MAR_CHR, buf); - buf_write(L, (void*)&rec_buf.head, MAR_I32, buf); - buf_write(L, rec_buf.data, rec_buf.head, buf); - buf_done(L, &rec_buf); - lua_pop(L, 1); - - lua_newtable(L); - for (i=1; i <= ar.nups; i++) { - lua_getupvalue(L, -2, i); - lua_rawseti(L, -2, i); - } - - buf_init(L, &rec_buf); - mar_encode_table(L, &rec_buf, idx); - - buf_write(L, (void*)&rec_buf.head, MAR_I32, buf); - buf_write(L, rec_buf.data, rec_buf.head, buf); - buf_done(L, &rec_buf); - lua_pop(L, 1); - } - - break; - } - case LUA_TUSERDATA: { - int tag, ref; - lua_pushvalue(L, -1); - lua_rawget(L, SEEN_IDX); - if (!lua_isnil(L, -1)) { - ref = lua_tointeger(L, -1); - tag = MAR_TREF; - buf_write(L, (void*)&tag, MAR_CHR, buf); - buf_write(L, (void*)&ref, MAR_I32, buf); - lua_pop(L, 1); - } - else { - mar_Buffer rec_buf; - lua_pop(L, 1); /* pop nil */ - if (luaL_getmetafield(L, -1, "__persist")) { - tag = MAR_TUSR; - - lua_pushvalue(L, -2); - lua_pushinteger(L, (*idx)++); - lua_rawset(L, SEEN_IDX); - - lua_pushvalue(L, -2); - lua_call(L, 1, 1); - if (!lua_isfunction(L, -1)) { - luaL_error(L, "__persist must return a function"); - } - lua_newtable(L); - lua_pushvalue(L, -2); - lua_rawseti(L, -2, 1); - lua_remove(L, -2); - - buf_init(L, &rec_buf); - mar_encode_table(L, &rec_buf, idx); - - buf_write(L, (void*)&tag, MAR_CHR, buf); - buf_write(L, (void*)&rec_buf.head, MAR_I32, buf); - buf_write(L, rec_buf.data, rec_buf.head, buf); - buf_done(L, &rec_buf); - } - else { - luaL_error(L, "attempt to encode userdata (no __persist hook)"); - } - lua_pop(L, 1); - } - break; - } - case LUA_TNIL: break; - default: - luaL_error(L, "invalid value type (%s)", lua_typename(L, val_type)); - } - lua_pop(L, 1); -} - -static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx) -{ - lua_pushnil(L); - while (lua_next(L, -2) != 0) { - mar_encode_value(L, buf, -2, idx); - mar_encode_value(L, buf, -1, idx); - lua_pop(L, 1); - } - return 1; -} - -#define mar_incr_ptr(l) \ - if (((*p)-buf)+(l) > len) luaL_error(L, "bad code"); (*p) += (l); - -#define mar_next_len(l,T) \ - if (((*p)-buf)+sizeof(T) > len) luaL_error(L, "bad code"); \ - l = *(T*)*p; (*p) += sizeof(T); - -static void mar_decode_value - (lua_State *L, const char *buf, size_t len, const char **p, size_t *idx) -{ - size_t l; - char val_type = **p; - mar_incr_ptr(MAR_CHR); - switch (val_type) { - case LUA_TBOOLEAN: - lua_pushboolean(L, *(char*)*p); - mar_incr_ptr(MAR_CHR); - break; - case LUA_TNUMBER: - lua_pushnumber(L, *(lua_Number*)*p); - mar_incr_ptr(MAR_I64); - break; - case LUA_TSTRING: - mar_next_len(l, uint32_t); - lua_pushlstring(L, *p, l); - mar_incr_ptr(l); - break; - case LUA_TTABLE: { - char tag = *(char*)*p; - mar_incr_ptr(MAR_CHR); - if (tag == MAR_TREF) { - int ref; - mar_next_len(ref, int); - lua_rawgeti(L, SEEN_IDX, ref); - } - else if (tag == MAR_TVAL) { - mar_next_len(l, uint32_t); - lua_newtable(L); - lua_pushvalue(L, -1); - lua_rawseti(L, SEEN_IDX, (*idx)++); - mar_decode_table(L, *p, l, idx); - mar_incr_ptr(l); - } - else if (tag == MAR_TUSR) { - mar_next_len(l, uint32_t); - lua_newtable(L); - mar_decode_table(L, *p, l, idx); - lua_rawgeti(L, -1, 1); - lua_call(L, 0, 1); - lua_remove(L, -2); - lua_pushvalue(L, -1); - lua_rawseti(L, SEEN_IDX, (*idx)++); - mar_incr_ptr(l); - } - else { - luaL_error(L, "bad encoded data"); - } - break; - } - case LUA_TFUNCTION: { - size_t nups; - int i; - mar_Buffer dec_buf; - char tag = *(char*)*p; - mar_incr_ptr(1); - if (tag == MAR_TREF) { - int ref; - mar_next_len(ref, int); - lua_rawgeti(L, SEEN_IDX, ref); - } - else { - mar_next_len(l, uint32_t); - dec_buf.data = (char*)*p; - dec_buf.size = l; - dec_buf.head = l; - dec_buf.seek = 0; - lua_load(L, (lua_Reader)buf_read, &dec_buf, "=marshal"); - mar_incr_ptr(l); - - lua_pushvalue(L, -1); - lua_rawseti(L, SEEN_IDX, (*idx)++); - - mar_next_len(l, uint32_t); - lua_newtable(L); - mar_decode_table(L, *p, l, idx); - nups = lua_objlen(L, -1); - for (i=1; i <= nups; i++) { - lua_rawgeti(L, -1, i); - lua_setupvalue(L, -3, i); - } - lua_pop(L, 1); - mar_incr_ptr(l); - } - break; - } - case LUA_TUSERDATA: { - char tag = *(char*)*p; - mar_incr_ptr(MAR_CHR); - if (tag == MAR_TREF) { - int ref; - mar_next_len(ref, int); - lua_rawgeti(L, SEEN_IDX, ref); - } - else if (tag == MAR_TUSR) { - mar_next_len(l, uint32_t); - lua_newtable(L); - mar_decode_table(L, *p, l, idx); - lua_rawgeti(L, -1, 1); - lua_call(L, 0, 1); - lua_remove(L, -2); - lua_pushvalue(L, -1); - lua_rawseti(L, SEEN_IDX, (*idx)++); - mar_incr_ptr(l); - } - else { /* tag == MAR_TVAL */ - lua_pushnil(L); - } - break; - } - case LUA_TNIL: - case LUA_TTHREAD: - lua_pushnil(L); - break; - default: - luaL_error(L, "bad code"); - } -} - -static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx) -{ - const char* p; - p = buf; - while (p - buf < len) { - mar_decode_value(L, buf, len, &p, idx); - mar_decode_value(L, buf, len, &p, idx); - lua_settable(L, -3); - } - return 1; -} - -static int mar_encode(lua_State* L) -{ - const unsigned char m = MAR_MAGIC; - size_t idx, len; - mar_Buffer buf; - - if (lua_isnone(L, 1)) { - lua_pushnil(L); - } - if (lua_isnoneornil(L, 2)) { - lua_newtable(L); - } - else if (!lua_istable(L, 2)) { - luaL_error(L, "bad argument #2 to encode (expected table)"); - } - lua_settop(L, 2); - - len = lua_objlen(L, 2); - lua_newtable(L); - for (idx = 1; idx <= len; idx++) { - lua_rawgeti(L, 2, idx); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); - continue; - } - lua_pushinteger(L, idx); - lua_rawset(L, SEEN_IDX); - } - lua_pushvalue(L, 1); - - buf_init(L, &buf); - buf_write(L, (void*)&m, 1, &buf); - - mar_encode_value(L, &buf, -1, &idx); - - lua_pop(L, 1); - - lua_pushlstring(L, buf.data, buf.head); - - buf_done(L, &buf); - - lua_remove(L, SEEN_IDX); - - return 1; -} - -static int mar_decode(lua_State* L) -{ - size_t l, idx, len; - const char *p; - const char *s = luaL_checklstring(L, 1, &l); - - if (l < 1) luaL_error(L, "bad header"); - if (*(unsigned char *)s++ != MAR_MAGIC) luaL_error(L, "bad magic"); - l -= 1; - - if (lua_isnoneornil(L, 2)) { - lua_newtable(L); - } - else if (!lua_istable(L, 2)) { - luaL_error(L, "bad argument #2 to decode (expected table)"); - } - lua_settop(L, 2); - - len = lua_objlen(L, 2); - lua_newtable(L); - for (idx = 1; idx <= len; idx++) { - lua_rawgeti(L, 2, idx); - lua_rawseti(L, SEEN_IDX, idx); - } - - p = s; - mar_decode_value(L, s, l, &p, &idx); - - lua_remove(L, SEEN_IDX); - lua_remove(L, 2); - - return 1; -} - -static int mar_clone(lua_State* L) -{ - mar_encode(L); - lua_replace(L, 1); - mar_decode(L); - return 1; -} - -static const luaL_reg R[] = -{ - {"encode", mar_encode}, - {"decode", mar_decode}, - {"clone", mar_clone}, - {NULL, NULL} -}; - -int luaopen_marshal(lua_State *L) -{ - lua_newtable(L); - luaL_register(L, "marshal", R); - return 1; -} - - - - - diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index 043ee4ba..12baac03 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -56,6 +56,9 @@ GameScripting::GameScripting(Server* server) // Create the main minetest table lua_newtable(L); + lua_setglobal(L, "minetest"); + lua_getglobal(L, "minetest"); + int top = lua_gettop(L); lua_newtable(L); lua_setfield(L, -2, "object_refs"); @@ -63,15 +66,11 @@ GameScripting::GameScripting(Server* server) lua_newtable(L); lua_setfield(L, -2, "luaentities"); - lua_setglobal(L, "minetest"); - // Initialize our lua_api modules - lua_getglobal(L, "minetest"); - int top = lua_gettop(L); InitializeModApi(L, top); lua_pop(L, 1); - infostream << "SCRIPTAPI: initialized game modules" << std::endl; + infostream << "SCRIPTAPI: Initialized game modules" << std::endl; } void GameScripting::InitializeModApi(lua_State *L, int top) diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp index a4619e9d..58ed6238 100644 --- a/src/script/scripting_mainmenu.cpp +++ b/src/script/scripting_mainmenu.cpp @@ -28,9 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc., extern "C" { #include "lualib.h" - int luaopen_marshal(lua_State *L); } -/******************************************************************************/ + +#define MAINMENU_ASYNC_THREADS 4 + + MainMenuScripting::MainMenuScripting(GUIEngine* guiengine) { setGuiEngine(guiengine); @@ -38,32 +40,31 @@ MainMenuScripting::MainMenuScripting(GUIEngine* guiengine) //TODO add security luaL_openlibs(getStack()); - luaopen_marshal(getStack()); SCRIPTAPI_PRECHECKHEADER + lua_newtable(L); + lua_setglobal(L, "engine"); + lua_getglobal(L, "engine"); + int top = lua_gettop(L); + lua_pushstring(L, DIR_DELIM); lua_setglobal(L, "DIR_DELIM"); lua_newtable(L); lua_setglobal(L, "gamedata"); - lua_newtable(L); - lua_setglobal(L, "engine"); - // Initialize our lua_api modules - lua_getglobal(L, "engine"); - int top = lua_gettop(L); InitializeModApi(L, top); lua_pop(L, 1); - infostream << "SCRIPTAPI: initialized mainmenu modules" << std::endl; + infostream << "SCRIPTAPI: Initialized main menu modules" << std::endl; } /******************************************************************************/ void MainMenuScripting::InitializeModApi(lua_State *L, int top) { - // Initialize mod api modules + // Initialize mod API modules ModApiMainMenu::Initialize(L, top); ModApiUtil::Initialize(L, top); @@ -76,16 +77,17 @@ void MainMenuScripting::InitializeModApi(lua_State *L, int top) // Initialize async environment //TODO possibly make number of async threads configurable - m_AsyncEngine.Initialize(MAINMENU_NUMBER_OF_ASYNC_THREADS); + m_AsyncEngine.Initialize(MAINMENU_ASYNC_THREADS); } /******************************************************************************/ void MainMenuScripting::Step() { - m_AsyncEngine.Step(getStack()); + m_AsyncEngine.Step(getStack(), m_errorhandler); } /******************************************************************************/ -unsigned int MainMenuScripting::DoAsync(std::string serialized_fct, - std::string serialized_params) { - return m_AsyncEngine.doAsyncJob(serialized_fct,serialized_params); +unsigned int MainMenuScripting::DoAsync(std::string serialized_func, + std::string serialized_param) { + return m_AsyncEngine.doAsyncJob(serialized_func, serialized_param); } + diff --git a/src/script/scripting_mainmenu.h b/src/script/scripting_mainmenu.h index f4d78f66..6a95d3fc 100644 --- a/src/script/scripting_mainmenu.h +++ b/src/script/scripting_mainmenu.h @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_base.h" #include "cpp_api/s_mainmenu.h" -#include "lua_api/l_async_events.h" +#include "cpp_api/s_async.h" /*****************************************************************************/ /* Scripting <-> Main Menu Interface */ @@ -35,14 +35,11 @@ class MainMenuScripting public: MainMenuScripting(GUIEngine* guiengine); - // use ScriptApiBase::loadMod() or ScriptApiBase::loadScript() - // to load scripts - - /* global step handler to pass back async events */ + // Global step handler to pass back async events void Step(); - /* pass async events from engine to async threads */ - unsigned int DoAsync(std::string serialized_fct, + // Pass async events from engine to async threads + unsigned int DoAsync(std::string serialized_func, std::string serialized_params); private: void InitializeModApi(lua_State *L, int top);