From 903974e7b3ceecb977449ac5ea34808de9501997 Mon Sep 17 00:00:00 2001 From: Nicolas Noble Date: Fri, 2 Aug 2013 15:53:08 -0700 Subject: Heavily revamped the C-to-Lua yielding mechanism. Now more generic. --- includes/BLua.h | 54 +++----------- includes/Exceptions.h | 2 +- includes/Task.h | 8 +- src/BLua.cc | 201 ++++++++++++++++++++++++++++++++++---------------- src/LuaTask.cc | 5 +- tests/test-Lua.cc | 74 ++++++++++++++----- 6 files changed, 210 insertions(+), 134 deletions(-) diff --git a/includes/BLua.h b/includes/BLua.h index 8f0993f..40e89bd 100644 --- a/includes/BLua.h +++ b/includes/BLua.h @@ -79,6 +79,7 @@ class Lua { Lua(); Lua(lua_State * __L) : L(__L) { } Lua(Lua && oL) : L(oL.L) { oL.L = NULL; } + Lua(const Lua & oL) : L(oL.L) { } Lua & operator=(Lua && oL); @@ -96,7 +97,7 @@ class Lua { void open_bit(); void open_jit(); void open_ffi(); - int wrap_open(openlualib_t open) { int n = gettop(); int r = open(L); while (n < gettop()) remove(n); return r; } + int wrap_open(openlualib_t open) { int n = gettop(); int r = open(L); while (n < gettop()) pop(); return r; } void openlib(const String & libname, const struct luaL_reg *l, int nup) { luaL_openlib(L, libname.to_charp(), l, nup); } void setCallWrap(lua_CallWrapper wrapper); @@ -118,10 +119,10 @@ class Lua { int checkstack(int extra = 1) { return lua_checkstack(L, extra); } int next(int t = -2) { return lua_next(L, t); } - void copy(int n = -1) { checkstack(); lua_pushvalue(L, n); } - void remove(int n = 1) { lua_remove(L, n); } - void insert(int n = 1) { checkstack(); lua_insert(L, n); } - void replace(int n = 1) { lua_replace(L, n); } + void copy(int i = -1) { checkstack(); lua_pushvalue(L, i); } + void remove(int i = 1) { lua_remove(L, i); } + void insert(int i = 1) { checkstack(); lua_insert(L, i); } + void replace(int i = 1) { lua_replace(L, i); } void newtable() { checkstack(); lua_newtable(L); } void * newuser(size_t s) { checkstack(); return lua_newuserdata(L, s); } void settable(int tableIdx = -3, bool raw = false); @@ -161,6 +162,7 @@ class Lua { void dumpvars(IO out, const String & prefix, int idx = -1); Lua thread(bool saveit = true); int yield(int nresults = 0) { return lua_yield(L, nresults); } + int yield(Future) throw (GeneralException) __attribute__((noreturn)); bool yielded() { return lua_status(L) == LUA_YIELD; } bool resume(int nargs = 0) throw (GeneralException); void showstack(int level = M_INFO); @@ -191,14 +193,14 @@ class Lua { protected: private: + void dumpvars_i(IO out, const String & prefix, int idx); void dumpvars_r(IO out, int idx, int depth = 0) throw (GeneralException); + bool resumeC(); + bool yieldC() throw (GeneralException); lua_State * L; friend class LuaStatics; - - Lua & operator=(const Lua &) = delete; - Lua(const Lua &) = delete; }; class LuaException : public GeneralException { @@ -339,16 +341,8 @@ template T * LuaObjectFactory::getMe(Lua & L, int idx) { return L.recast(idx); } class LuaHelpersBase { - public: - static bool resume(Lua & L); - static Events::BaseEvent * getEvent(Lua & L); protected: static void validate(const lua_functypes_t & entry, bool method, int n, Lua & L, const char * className); - static void pushContext(Lua & L, std::function context, Events::BaseEvent * evt); - private: - LuaHelpersBase(std::function context, Events::BaseEvent * evt) : m_context(context), m_evt(evt) { } - std::function m_context; - Events::BaseEvent * m_evt; }; template @@ -375,34 +369,6 @@ class LuaHelpers : public LuaHelpersBase { private: static int method_multiplex(int caller, Lua & L, proceed_t proceed, proceed_static_t proceed_static, lua_functypes_t * tab, bool method) { - int r; - - try { - r = method_multiplex_internal(caller, L, proceed, proceed_static, tab, method); - } - catch (EAgain & e) { - pushContext(L, [caller, proceed, proceed_static, tab, method](Lua & L) -> int { return method_multiplex_resume(caller, L, proceed, proceed_static, tab, method); }, e.getEvent()); - r = L.yield(L.gettop()); - } - - return r; - } - - static int method_multiplex_resume(int caller, Lua & L, proceed_t proceed, proceed_static_t proceed_static, lua_functypes_t * tab, bool method) { - int r; - - try { - r = method_multiplex_internal(caller, L, proceed, proceed_static, tab, method); - } - catch (EAgain & e) { - pushContext(L, [caller, proceed, proceed_static, tab, method](Lua & L) -> int { return method_multiplex_resume(caller, L, proceed, proceed_static, tab, method); }, e.getEvent()); - r = -1; - } - - return r; - } - - static int method_multiplex_internal(int caller, Lua & L, proceed_t proceed, proceed_static_t proceed_static, lua_functypes_t * tab, bool method) { int add = method ? 1 : 0; int n = L.gettop() - add; T * obj = 0; diff --git a/includes/Exceptions.h b/includes/Exceptions.h index 2d452cc..729f523 100644 --- a/includes/Exceptions.h +++ b/includes/Exceptions.h @@ -34,7 +34,7 @@ class GeneralException { const std::vector getTrace() const { return m_trace; } protected: - GeneralException() { } + explicit GeneralException() { } void setMsg(char * msg) { if (m_msg) free(m_msg); m_msg = msg; } void genTrace(); diff --git a/includes/Task.h b/includes/Task.h index 4334ad2..3219397 100644 --- a/includes/Task.h +++ b/includes/Task.h @@ -269,8 +269,10 @@ class Future { R get(); void run(); private: + friend class Lua; func_t m_func; Events::BaseEvent * m_evt = NULL; + bool m_ranOnce = false; }; template @@ -278,9 +280,10 @@ R Future::get() { R r; for (;;) { if (m_evt && !m_evt->gotSignal()) - continue; + Task::operationYield(m_evt, Task::INTERRUPTIBLE); m_evt = NULL; try { + m_ranOnce = true; r = m_func(); return r; } @@ -295,9 +298,10 @@ template void Future::run() { for (;;) { if (m_evt && !m_evt->gotSignal()) - continue; + Task::operationYield(m_evt, Task::INTERRUPTIBLE); m_evt = NULL; try { + m_ranOnce = true; m_func(); return; } diff --git a/src/BLua.cc b/src/BLua.cc index 9a2140f..29f4a58 100644 --- a/src/BLua.cc +++ b/src/BLua.cc @@ -2,6 +2,7 @@ #include "BLua.h" #include "Printer.h" #include "Input.h" +#include "Buffer.h" extern "C" { #include @@ -12,6 +13,16 @@ extern "C" { #define BUFFERSIZE 2048 #endif +namespace { + +class LuaYield : public Balau::GeneralException { + public: + LuaYield(Balau::Future & f) : GeneralException(), m_f(f) { } + Balau::Future m_f; +}; + +}; + namespace Balau { class LuaStatics { @@ -204,15 +215,24 @@ int Balau::LuaStatics::print(lua_State * __L) { return 0; } +static char s_signature = 'B'; + int Balau::LuaStatics::callwrap(lua_State * __L, lua_CFunction func) { Lua L(__L); + int r = 0; try { - return func(__L); + r = func(__L); } catch (LuaException & e) { L.error(String("LuaException: ") + e.getMsg()); } + catch (LuaYield & y) { + Future * f = new Future(y.m_f); + L.push((void *) f); + L.push((void *) &s_signature); + r = L.yield(L.gettop()); + } catch (Balau::GeneralException & e) { L.error(String("GeneralException: ") + e.getMsg()); } @@ -221,7 +241,7 @@ int Balau::LuaStatics::callwrap(lua_State * __L, lua_CFunction func) { // L.error("Unknown C++ exception"); // } - return 0; + return r; } int Balau::LuaStatics::collector(lua_State * __L) { @@ -312,7 +332,7 @@ void Balau::Lua::open_base() { push("print"); push(LuaStatics::print); settable(); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); push("mkdir"); push(LuaStatics::mkdir); settable(LUA_GLOBALSINDEX); @@ -336,7 +356,7 @@ void Balau::Lua::open_base() { void Balau::Lua::open_table() { int n = gettop(); luaopen_table(L); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); } void Balau::Lua::open_string() { @@ -345,37 +365,37 @@ void Balau::Lua::open_string() { push("iconv"); push(LuaStatics::iconv); settable(); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); } void Balau::Lua::open_math() { int n = gettop(); luaopen_math(L); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); } void Balau::Lua::open_debug() { int n = gettop(); luaopen_debug(L); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); } void Balau::Lua::open_jit() { int n = gettop(); luaopen_jit(L); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); } void Balau::Lua::open_ffi() { int n = gettop(); luaopen_ffi(L); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); } void Balau::Lua::open_bit() { int n = gettop(); luaopen_bit(L); - while (n < gettop()) remove(-1); + while (n < gettop()) pop(); } void Balau::Lua::declareFunc(const char * name, lua_CFunction f, int i) { @@ -564,7 +584,15 @@ void Balau::Lua::load(const String & s, bool docall) throw (GeneralException) { } void Balau::Lua::dumpvars(IO h, const String & prefix, int i) { - Task::SimpleContext sc; + if (h.isA()) { + dumpvars_i(h, prefix, i); + } else { + Task::SimpleContext sc; + dumpvars_i(h, prefix, i); + } +} + +void Balau::Lua::dumpvars_i(IO h, const String & prefix, int i) { h->writeString(prefix); h->writeString(" = {\n"); dumpvars_r(h, i); @@ -706,10 +734,22 @@ Balau::Lua Balau::Lua::thread(bool saveit) { bool Balau::Lua::resume(int nargs) throw (GeneralException) { int r; + if (resumeC()) { + if (yielded()) { + yieldC(); + return true; + } else { + return false; + } + } + r = lua_resume(L, nargs); - if ((r == 0) || (r == LUA_YIELD)) - return 0; + if (r == LUA_YIELD) + return yieldC(); + + if (r == 0) + return false; pushLuaContext(); showerror(); @@ -732,6 +772,90 @@ bool Balau::Lua::resume(int nargs) throw (GeneralException) { } } +bool Balau::Lua::resumeC() { + if (!yielded()) + return false; + + if (gettop() < 2) + return false; + + if (!islightuserdata(-1)) + return false; + + if (!islightuserdata(-2)) + return false; + + void * s = touserdata(); + if (s != &s_signature) + return false; + + pop(); + + Future * p = (Future *) touserdata(); + Future f(*p); + delete p; + pop(); + + int nargs = 0; + bool yieldedAgain = false; + + try { + nargs = f.get(); + } + catch (LuaException & e) { + error(String("LuaException: ") + e.getMsg()); + } + catch (EAgain & e) { + Future * p = new Future(f); + p->m_evt = e.getEvent(); + push((void *) p); + push((void *) &s_signature); + yieldedAgain = true; + } + catch (LuaYield & y) { + Future * p = new Future(y.m_f); + push((void *) p); + push((void *) &s_signature); + yieldedAgain = true; + } + catch (Balau::GeneralException & e) { + error(String("GeneralException: ") + e.getMsg()); + } + if (!yieldedAgain) + resume(nargs); + return true; +} + +int Balau::Lua::yield(Future f) throw (GeneralException) { + throw LuaYield(f); +} + +bool Balau::Lua::yieldC() throw (GeneralException) { + if (!yielded()) + return true; + + if (gettop() < 2) + return true; + + if (!islightuserdata(-1)) + return true; + + if (!islightuserdata(-2)) + return true; + + void * s = touserdata(); + if (s != &s_signature) + return true; + + Future * p = (Future *) touserdata(-2); + + if (p->m_ranOnce) + throw EAgain(p->m_evt); + + resumeC(); + return yieldC(); +} + void Balau::Lua::showstack(int level) { int n = lua_gettop(L); int i; @@ -923,54 +1047,3 @@ void Balau::LuaHelpersBase::validate(const lua_functypes_t & entry, bool method, } } } - -static char s_signature = 'B'; - -void Balau::LuaHelpersBase::pushContext(Lua & L, std::function context, Events::BaseEvent * evt) { - L.push(new LuaHelpersBase(context, evt)); - L.push((void *) &s_signature); -} - -bool Balau::LuaHelpersBase::resume(Lua & L) { - if (L.gettop() < 2) - return false; - - if (!L.islightuserdata()) - return false; - - char * sig = (char *) L.touserdata(); - if (sig != &s_signature) - return false; - - L.remove(-1); - - LuaHelpersBase * b = (LuaHelpersBase *) L.touserdata(); - L.remove(-1); - - int r = b->m_context(L); - - delete b; - - if (r < 0) - return true; - - L.resume(r); - - return true; -} - -Balau::Events::BaseEvent * Balau::LuaHelpersBase::getEvent(Lua & L) { - if (L.gettop() < 2) - return NULL; - - if (!L.islightuserdata()) - return NULL; - - char * sig = (char *) L.touserdata(); - if (sig != &s_signature) - return NULL; - - LuaHelpersBase * b = (LuaHelpersBase *) L.touserdata(-2); - - return b->m_evt; -} diff --git a/src/LuaTask.cc b/src/LuaTask.cc index fa94621..5c03521 100644 --- a/src/LuaTask.cc +++ b/src/LuaTask.cc @@ -65,7 +65,7 @@ void Balau::LuaTask::Do() { while(true) { try { if (L.yielded()) - LuaHelpersBase::resume(L); + L.resume(); else m_cell->run(L); } @@ -76,9 +76,6 @@ void Balau::LuaTask::Do() { m_cell->setError(); } if (L.yielded()) { - Events::BaseEvent * evt = LuaHelpersBase::getEvent(L); - IAssert(evt, "We need an event for now here."); - waitFor(evt); yield(); continue; } diff --git a/tests/test-Lua.cc b/tests/test-Lua.cc index 06f233b..ed6e754 100644 --- a/tests/test-Lua.cc +++ b/tests/test-Lua.cc @@ -1,6 +1,8 @@ #include #include #include +#include +#include using namespace Balau; @@ -88,7 +90,8 @@ int sLua_ObjectTest::ObjectTest_proceed(Lua & L, int n, ObjectTest * obj, int ca return 0; } -Events::Timeout * evt = NULL; +static Events::Timeout * evt = NULL; +static int yieldCount = 0; int sLua_ObjectTest::ObjectTest_proceed_static(Lua & L, int n, int caller) throw (GeneralException) { int y; @@ -112,23 +115,57 @@ int sLua_ObjectTest::ObjectTest_proceed_static(Lua & L, int n, int caller) throw break; case OBJECTTEST_YIELDTEST: - y = L.tonumber(); - L.remove(); - L.push((lua_Number) y + 1); - Printer::log(M_STATUS, "yield %i", y); - if (evt) - delete evt; - evt = NULL; - if (y < 2) { - evt = new Events::Timeout(0.1f); - throw EAgain(evt); - } + L.yield(Future([=]() mutable { + int y = L.tonumber(); + L.pop(); + L.push((lua_Number) y + 1); + Printer::log(M_STATUS, "yield %i", y); + if (evt) + delete evt; + evt = NULL; + yieldCount++; + if (y < 2) { + evt = new Events::Timeout(0.1f); + Task::operationYield(evt, Task::INTERRUPTIBLE); + } + + L.push(true); + return 1; + })); break; } return 0; } +class StacklessYieldTest : public StacklessTask { + public: + StacklessYieldTest(Lua & __L) : L(__L) { } + virtual const char * getName() const override { return "StacklessYieldTest"; } + private: + Lua & L; + bool ranOnce = false; + virtual void Do() override { + try { + if (ranOnce) { + L.resume(); + } else { + ranOnce = true; + L.load("return yieldTest(0)"); + } + TAssert(!L.yielded()); + TAssert(L.gettop() == 1); + TAssert(L.isboolean()); + TAssert(L.toboolean()); + TAssert(yieldCount == 3); + L.pop(); + } + catch (EAgain & e) { + taskSwitch(); + } + } +}; + void MainTask::Do() { Printer::log(M_STATUS, "Test::Lua running."); @@ -194,13 +231,12 @@ void MainTask::Do() { TAssert(callCount == 4); - L.load("yieldTest(0)"); - while (L.yielded()) { - waitFor(LuaHelpersBase::getEvent(L)); - yield(); - LuaHelpersBase::resume(L); - } - TAssert(L.gettop() == 0); + Task * syt = new StacklessYieldTest(L); + Events::TaskEvent evt(syt); + waitFor(&evt); + TaskMan::registerTask(syt, this); + yield(); + evt.ack(); L.load("return obj.__type.name == 'ObjectTest', obj.__type.new == ObjectTest.new"); TAssert(L.gettop() == 2); -- cgit v1.2.3