summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas "Pixel" Noble <pixel@nobis-crew.org>2013-07-25 10:58:33 +0200
committerNicolas "Pixel" Noble <pixel@nobis-crew.org>2013-07-25 10:58:33 +0200
commit8bb55a25830c3f7d2c67c8571786b6806fb8f515 (patch)
tree9ff9531f33c82fa1dd23695982c8d1f53dcbd1ad
parentb92f0cee87ae48608c279d7192872b83a29b8fc5 (diff)
Lua now properly yields when an operation throws EAgain.
-rw-r--r--includes/BLua.h44
-rw-r--r--src/BLua.cc51
-rw-r--r--src/LuaTask.cc37
-rw-r--r--tests/test-Lua.cc34
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 <functional>
+
extern "C" {
#include <lua.h>
#include <lauxlib.h>
@@ -7,6 +9,7 @@ extern "C" {
#include <Exceptions.h>
#include <Handle.h>
+#include <Task.h>
namespace Balau {
@@ -271,8 +274,16 @@ template <class T>
T * LuaObjectFactory::getMe(Lua & L, int idx) { return L.recast<T>(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<int(Lua & L)> context, Events::BaseEvent * evt);
+ private:
+ LuaHelpersBase(std::function<int(Lua & L)> context, Events::BaseEvent * evt) : m_context(context), m_evt(evt) { }
+ std::function<int(Lua & L)> m_context;
+ Events::BaseEvent * m_evt;
};
template <class T>
@@ -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<int(Lua & L)> 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);