From 8bb55a25830c3f7d2c67c8571786b6806fb8f515 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Thu, 25 Jul 2013 10:58:33 +0200 Subject: Lua now properly yields when an operation throws EAgain. --- includes/BLua.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/BLua.cc | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/LuaTask.cc | 37 +++++++++++++++++++++++++------------ tests/test-Lua.cc | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 152 insertions(+), 14 deletions(-) diff --git a/includes/BLua.h b/includes/BLua.h index bf7caae..e11c3fb 100644 --- a/includes/BLua.h +++ b/includes/BLua.h @@ -1,5 +1,7 @@ #pragma once +#include + extern "C" { #include #include @@ -7,6 +9,7 @@ extern "C" { #include #include +#include namespace Balau { @@ -271,8 +274,16 @@ 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 @@ -280,6 +291,39 @@ class LuaHelpers : public LuaHelpersBase { public: static int method_multiplex(int caller, lua_State * __L, int (*proceed)(Lua & L, int n, T * obj, int caller), int (*proceed_static)(Lua & L, int n, int caller), lua_functypes_t * tab, bool method) { Lua L(__L); + return method_multiplex(caller, L, proceed, proceed_static, tab, method); + } + + private: + static int method_multiplex(int caller, Lua & L, int (*proceed)(Lua & L, int n, T * obj, int caller), int (*proceed_static)(Lua & L, int n, int caller), 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, int (*proceed)(Lua & L, int n, T * obj, int caller), int (*proceed_static)(Lua & L, int n, int caller), 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, int (*proceed)(Lua & L, int n, T * obj, int caller), int (*proceed_static)(Lua & L, int n, int caller), lua_functypes_t * tab, bool method) { int add = method ? 1 : 0; int n = L.gettop() - add; T * obj = 0; diff --git a/src/BLua.cc b/src/BLua.cc index 1ce20e6..62e4010 100644 --- a/src/BLua.cc +++ b/src/BLua.cc @@ -908,3 +908,54 @@ 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 fc92cdf..f94a2b4 100644 --- a/src/LuaTask.cc +++ b/src/LuaTask.cc @@ -57,19 +57,32 @@ void Balau::LuaMainTask::Do() { } void Balau::LuaTask::Do() { - try { - m_cell->run(L); - } - catch (GeneralException e) { - m_cell->m_exception = new GeneralException(e); - } - catch (...) { - m_cell->setError(); + while(true) { + try { + if (L.yielded()) + LuaHelpersBase::resume(L); + else + m_cell->run(L); + } + catch (GeneralException e) { + m_cell->m_exception = new GeneralException(e); + } + catch (...) { + 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; + } + if (m_cell->m_detached) + delete m_cell; + else + m_cell->m_event.trigger(); + break; } - if (m_cell->m_detached) - delete m_cell; - else - m_cell->m_event.trigger(); } void Balau::LuaExecCell::exec(LuaMainTask * mainTask) { diff --git a/tests/test-Lua.cc b/tests/test-Lua.cc index 39ccbee..93b0f32 100644 --- a/tests/test-Lua.cc +++ b/tests/test-Lua.cc @@ -24,6 +24,7 @@ enum ObjectTest_methods_t { enum ObjectTest_functions_t { OBJECTTEST_CREATEOBJECTTEST, OBJECTTEST_SOMEFUNCTION, + OBJECTTEST_YIELDTEST, }; struct lua_functypes_t ObjectTest_methods[] = { @@ -35,6 +36,7 @@ struct lua_functypes_t ObjectTest_methods[] = { struct lua_functypes_t ObjectTest_functions[] = { { OBJECTTEST_CREATEOBJECTTEST, "createObjectTest", 0, 0, { } }, { OBJECTTEST_SOMEFUNCTION, "ObjectTestFunction", 0, 0, { } }, + { OBJECTTEST_YIELDTEST, "yieldTest", 1, 1, { BLUA_NUMBER } }, { -1, 0, 0, 0, 0 }, }; @@ -45,9 +47,10 @@ class sLua_ObjectTest { DECLARE_FUNCTION(ObjectTest, OBJECTTEST_CREATEOBJECTTEST); DECLARE_FUNCTION(ObjectTest, OBJECTTEST_SOMEFUNCTION); + DECLARE_FUNCTION(ObjectTest, OBJECTTEST_YIELDTEST); private: static int ObjectTest_proceed(Lua & L, int n, ObjectTest * obj, int caller); - static int ObjectTest_proceed_statics(Lua & L, int n, int caller); + static int ObjectTest_proceed_statics(Lua & L, int n, int caller) throw (GeneralException); }; class LuaObjectTestFactory : public LuaObjectFactory { @@ -59,6 +62,7 @@ class LuaObjectTestFactory : public LuaObjectFactory { PUSH_FUNCTION(ObjectTest, OBJECTTEST_CREATEOBJECTTEST); PUSH_FUNCTION(ObjectTest, OBJECTTEST_SOMEFUNCTION); + PUSH_FUNCTION(ObjectTest, OBJECTTEST_YIELDTEST); } private: void pushObjectAndMembers(Lua & L) { @@ -85,7 +89,11 @@ int sLua_ObjectTest::ObjectTest_proceed(Lua & L, int n, ObjectTest * obj, int ca return 0; } -int sLua_ObjectTest::ObjectTest_proceed_statics(Lua & L, int n, int caller) { +Events::Timeout * evt = NULL; + +int sLua_ObjectTest::ObjectTest_proceed_statics(Lua & L, int n, int caller) throw (GeneralException) { + int y; + switch (caller) { case OBJECTTEST_CREATEOBJECTTEST: { @@ -99,6 +107,20 @@ int sLua_ObjectTest::ObjectTest_proceed_statics(Lua & L, int n, int caller) { case OBJECTTEST_SOMEFUNCTION: ObjectTest::someFunction(); 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 < 5) { + evt = new Events::Timeout(1.0f); + throw EAgain(evt); + } + break; } return 0; @@ -164,6 +186,14 @@ void MainTask::Do() { TAssert(callCount == 3); + L.load("yieldTest(0)"); + while (L.yielded()) { + waitFor(LuaHelpersBase::getEvent(L)); + yield(); + LuaHelpersBase::resume(L); + } + TAssert(L.gettop() == 0); + TAssert(objGotDestroyed == 0); L.load("obj2 = createObjectTest() obj2:destroy()"); TAssert(objGotDestroyed == 1); -- cgit v1.2.3