summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Noble <pixel@nobis-crew.org>2013-08-02 15:53:08 -0700
committerNicolas Noble <pixel@nobis-crew.org>2013-08-02 15:53:08 -0700
commit903974e7b3ceecb977449ac5ea34808de9501997 (patch)
tree1c61d4574712a95a106c8647084a95b91d529a5b
parentf416d651f3d6551aa0efbcdb8b5838269de9bff3 (diff)
Heavily revamped the C-to-Lua yielding mechanism. Now more generic.
-rw-r--r--includes/BLua.h54
-rw-r--r--includes/Exceptions.h2
-rw-r--r--includes/Task.h8
-rw-r--r--src/BLua.cc201
-rw-r--r--src/LuaTask.cc5
-rw-r--r--tests/test-Lua.cc74
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<Handle> 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<int>) 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<Handle> out, const String & prefix, int idx);
void dumpvars_r(IO<Handle> 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 <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>
@@ -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<String> 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<class R>
@@ -278,9 +280,10 @@ R Future<R>::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<class R>
void Future<R>::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 <lualib.h>
@@ -12,6 +13,16 @@ extern "C" {
#define BUFFERSIZE 2048
#endif
+namespace {
+
+class LuaYield : public Balau::GeneralException {
+ public:
+ LuaYield(Balau::Future<int> & f) : GeneralException(), m_f(f) { }
+ Balau::Future<int> 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<int> * f = new Future<int>(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<Handle> h, const String & prefix, int i) {
- Task::SimpleContext sc;
+ if (h.isA<Buffer>()) {
+ dumpvars_i(h, prefix, i);
+ } else {
+ Task::SimpleContext sc;
+ dumpvars_i(h, prefix, i);
+ }
+}
+
+void Balau::Lua::dumpvars_i(IO<Handle> 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<int> * p = (Future<int> *) touserdata();
+ Future<int> 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<int> * p = new Future<int>(f);
+ p->m_evt = e.getEvent();
+ push((void *) p);
+ push((void *) &s_signature);
+ yieldedAgain = true;
+ }
+ catch (LuaYield & y) {
+ Future<int> * p = new Future<int>(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<int> 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<int> * p = (Future<int> *) 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<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 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 <Main.h>
#include <BLua.h>
#include <Input.h>
+#include <StacklessTask.h>
+#include <TaskMan.h>
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<int>([=]() 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);