From 1f97ba72e51f52f351137550239e9dae7f743fc7 Mon Sep 17 00:00:00 2001 From: pixel Date: Thu, 9 Feb 2006 17:08:24 +0000 Subject: In general: better thread handling. Added locks everywhere it should. Added/fixed thread spawning stuff. Also, added Lua's methods: -) copy -) setgcthreshold -) getgcthreshold -) getgccount --- include/BLua.h | 35 ++++-- lib/BLua.cc | 344 +++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 302 insertions(+), 77 deletions(-) diff --git a/include/BLua.h b/include/BLua.h index 77c3a9d..9fa4248 100644 --- a/include/BLua.h +++ b/include/BLua.h @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* $Id: BLua.h,v 1.27 2006-01-31 17:02:38 pixel Exp $ */ +/* $Id: BLua.h,v 1.28 2006-02-09 17:08:24 pixel Exp $ */ #ifndef __BLUA_H__ #define __BLUA_H__ @@ -33,6 +33,7 @@ extern "C" { #define lua_unlock(L) do_lua_unlock(L) #include +#include #include #include #include @@ -66,6 +67,7 @@ class Lua : public Base { void push(void *); void push(lua_CFunction, int = 0); void pop(int = 1); + void copy(int = -1); void newtable(); void * newuser(size_t); void settable(int = -3, bool raw = false); @@ -94,11 +96,16 @@ class Lua : public Base { Lua * tothread(int = -1); String escape_string(const String &); void load(Handle *, bool docall = true) throw (GeneralException); + void load(const String &, bool docall = true) throw (GeneralException); void dump(Handle *, bool strip = true); void dumpvars(Handle *, const String &, int = -1); - Lua * thread(); - int yield(int nargs = 0); - int resume(int nresults = 0); + Lua * thread(bool saveit = true); + Lua * thread(const String &, int nargs = 0, bool saveit = true); + Lua * thread(Handle *, int nargs = 0, bool saveit = true); + int yield(int nresults = 0); + void resume(int nargs = 0) throw (GeneralException); + void resume(const String &, int nargs = 0); + void resume(Handle *, int nargs = 0); static Lua * find(lua_State *) throw (GeneralException); void showerror(); int getmetatable(int = -1); @@ -107,16 +114,28 @@ class Lua : public Base { void do_break(); - virtual void lock() {} - virtual void unlock() {} + virtual void lock(); + virtual void unlock(); bool is_protected(); - private: + void openlib(const String & libname, const struct luaL_reg *l, int nup); + + void setgcthreshold(int = 0); + int getgcthreshold(); + int getgccount(); + + protected: + virtual Lua * spawn_from_thread(lua_State *); Lua(lua_State *); + + private: + void setup_state(lua_State *); lua_State * L; static std::map lualist; - bool _protected; + bool _protected, _is_thread; void dumpvars_r(Handle *, int, int = 0) throw (GeneralException); + + friend class LuaStatics; }; class LuaObject : public Base { diff --git a/lib/BLua.cc b/lib/BLua.cc index 061a1f6..d86740d 100644 --- a/lib/BLua.cc +++ b/lib/BLua.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* $Id: BLua.cc,v 1.35 2006-02-02 14:09:48 pixel Exp $ */ +/* $Id: BLua.cc,v 1.36 2006-02-09 17:08:24 pixel Exp $ */ #include #include "BLua.h" @@ -60,6 +60,8 @@ class LuaStatics : public Base { static int trueluapanic(lua_State *) throw(GeneralException); static int luaerror(lua_State *); static int callwrap(lua_State *, lua_CFunction); + static void createhook(lua_State *, lua_State *); + static void destroyhook(lua_State *, lua_State *); static int collector(lua_State *); static int destructor(lua_State *); @@ -75,14 +77,18 @@ class LuaStatics : public Base { static int dumpvars(lua_State *); static int iconv(lua_State *); + + static int globalindex(lua_State *); }; std::map Lua::lualist; int LuaStatics::luaerror(lua_State * __L) { Lua * L = Lua::find(__L); + L->lock(); L->push_lua_context(); L->showerror(); + L->unlock(); return 0; } @@ -94,9 +100,11 @@ int LuaStatics::trueluapanic(lua_State * __L) throw (GeneralException) { Lua * L = Lua::find(__L); if (L->is_protected()) return 0; // luaerror will get it for us... + L->lock(); L->push_lua_context(); L->showerror(); - throw LuaException("Unprotected error running Lua code, bailing out; except unstable lua environment."); + L->unlock(); + throw LuaException("Unprotected error running Lua code, bailing out; expect unstable lua environment."); } int LuaStatics::andB(lua_State * __L) { @@ -236,8 +244,10 @@ int LuaStatics::getglobal(lua_State * _L) { Buffer b; b << "return " + L->tostring(); + L->lock(); L->load(&b, false); L->call(0, 1); + L->unlock(); return 1; } @@ -289,11 +299,79 @@ int LuaStatics::iconv(lua_State * _L) { return 1; } -Lua::Lua() : L(lua_open()) { +int LuaStatics::callwrap(lua_State * __L, lua_CFunction func) { + Lua * L = Lua::find(__L); + int n; + + try { + n = func(__L); + } + catch (LuaException e) { + L->error(String("LuaException: ") + e.GetMsg()); + } + catch (GeneralException e) { + L->error(String("GeneralException: ") + e.GetMsg()); + } + + return n; +} + +int LuaStatics::collector(lua_State * __L) { + Lua * L = Lua::find(__L); + void ** u = (void **) L->touserdata(); + bool * obj = (bool *) (u + 1); +// printm(M_INFO, "From LUA: collecting object\n"); + if (*obj) { +// printm(M_INFO, "Is object at %p\n", *u); + Base * b = (Base *) *u; + delete b; + } else { +// printm(M_INFO, "Is struct at %p\n", *u); + free(*u); + } + *u = 0; + return 0; +} + +int LuaStatics::destructor(lua_State * __L) { + Lua * L = Lua::find(__L); + Base * b = (Base *) LuaObject::getme(L); + delete b; + L->push("__obj"); + L->gettable(-2, true); + void ** u = (void **) L->touserdata(); + bool * obj = (bool *) (u + 1); + if (*obj) { + Base * b = (Base *) *u; + delete b; + } else { + free(*u); + } + *u = 0; + L->pop(); + return 0; +} + +void LuaStatics::createhook(lua_State * __L, lua_State * L1) { + Lua * L = Lua::find(__L); + L->spawn_from_thread(L1); +} + +void LuaStatics::destroyhook(lua_State * __L, lua_State * L1) { + Lua * L = Lua::find(L1); + delete L; +} + +void Lua::setup_state(lua_State *) { lualist[L] = this; lua_atpanic(L, LuaStatics::luapanic); lua_setcallwrap(L, LuaStatics::callwrap); + lua_setcreatehook(L, LuaStatics::createhook); + lua_setdestroyhook(L, LuaStatics::destroyhook); +} +Lua::Lua() : L(lua_open()), _protected(false), _is_thread(false) { + setup_state(L); declarefunc("andB", LuaStatics::andB); declarefunc("orB", LuaStatics::orB); declarefunc("xorB", LuaStatics::xorB); @@ -303,16 +381,31 @@ Lua::Lua() : L(lua_open()) { declarefunc("hex", LuaStatics::hex); declarefunc("getglobal", LuaStatics::getglobal); declarefunc("dumpvars", LuaStatics::dumpvars); + push("BLUA_THREADS"); + newtable(); + settable(LUA_REGISTRYINDEX); } -Lua::Lua(lua_State * __L) : L(__L), _protected(false) { - lualist[L] = this; - lua_atpanic(L, LuaStatics::luapanic); +Lua::Lua(lua_State * __L) : L(__L), _protected(false), _is_thread(true) { + setup_state(L); + + // **TODO** + // do we need to define global functions as well.. ? + // I don't think so. Environment should be duplicated. + +} + +Lua * Lua::spawn_from_thread(lua_State * __L) { + return new Lua(__L); } Lua::~Lua() { - lua_setgcthreshold(L, 0); - lua_close(L); + if (!_is_thread) { + lua_setgcthreshold(L, 0); + lua_close(L); + } + + L = 0; } bool Lua::is_protected() { @@ -324,65 +417,85 @@ Lua::Lua(const Lua & l) throw (GeneralException) { } void Lua::open_base() { + lock(); luaopen_base(L); lua_pop(L, 1); + unlock(); } void Lua::open_table() { + lock(); luaopen_table(L); lua_pop(L, 1); + unlock(); } void Lua::open_io() { + lock(); luaopen_io(L); lua_pop(L, 1); + unlock(); } void Lua::open_string() { + lock(); luaopen_string(L); push("iconv"); push(LuaStatics::iconv); settable(); lua_pop(L, 1); + unlock(); } void Lua::open_math() { + lock(); luaopen_math(L); lua_pop(L, 1); + unlock(); } void Lua::open_debug() { + lock(); luaopen_debug(L); lua_pop(L, 1); + unlock(); } void Lua::open_dir() { + lock(); luaopen_dir(L); lua_pop(L, 1); + unlock(); } void Lua::declarefunc(const String & name, lua_CFunction f, int i) { + lock(); lua_pushstring(L, name.to_charp()); lua_pushcfunction(L, f); lua_settable(L, i); + unlock(); } void Lua::call(const String & f, int i, int nargs, int nresults) { + lock(); lua_pushstring(L, f.to_charp()); lua_gettable(L, i); lua_insert(L, -1 - nargs - nresults); call(nargs, nresults); + unlock(); } void Lua::call(int nargs, int nresults) throw(GeneralException) { int r; + lock(); lua_pushcfunction(L, LuaStatics::luaerror); lua_insert(L, 1); _protected = true; r = lua_pcall(L, nargs, nresults, 1); _protected = false; lua_remove(L, 1); + unlock(); switch(r) { case 0: @@ -434,6 +547,10 @@ void Lua::pop(int n) { lua_pop(L, n); } +void Lua::copy(int n) { + lua_pushvalue(L, n); +} + void Lua::newtable() { lua_newtable(L); } @@ -471,8 +588,10 @@ void Lua::getglobal(const String & name) throw (GeneralException) { b << "return " + name; try { + lock(); load(&b, false); call(0, 1); + unlock(); } catch (LuaException &) { throw LuaException("Error finding global variable `" + name + "'"); @@ -547,6 +666,7 @@ bool Lua::islightuserdata(int i) { bool Lua::isobject(int i) { bool r = false; + lock(); if (istable(i)) { push("__obj"); gettable(i); @@ -555,6 +675,7 @@ bool Lua::isobject(int i) { } else { r = isnil(i); } + unlock(); return r; } @@ -615,8 +736,25 @@ int LuaStatics::putF(lua_State * L, const void * p, size_t size, void * ud) { } String Lua::escape_string(const String & s) { - /* TODO */ - return s; + String r = ""; + int i; + for (i = 0; i < s.strlen(); i++) { + switch(s[i]) { + case '"': case '\\': + r += '\\'; + r += s[i]; + break; + case '\n': + r += "\n"; + break; + case '\0': + r += "\\000"; + break; + default: + r += s[i]; + } + } + return r; } void Lua::load(Handle * h, bool docall) throw (GeneralException) { @@ -625,6 +763,8 @@ void Lua::load(Handle * h, bool docall) throw (GeneralException) { lf.f = h; + if (docall) + lock(); status = lua_load(L, LuaStatics::getF, &lf, h->GetName().to_charp()); if (status) { @@ -633,8 +773,31 @@ void Lua::load(Handle * h, bool docall) throw (GeneralException) { throw LuaException("Error loading lua chunk from Handle `" + h->GetName() + "'"); } + if (docall) { + call(); + unlock(); + } +} + +void Lua::load(const String & s, bool docall) throw (GeneralException) { + const char * buf = s.to_charp(); + int status; + if (docall) + lock(); + + status = luaL_loadbuffer(L, buf, s.strlen(), buf); + + if (status) { + push_lua_context(); + showerror(); + throw LuaException("Error loading lua string `" + s + "'"); + } + + if (docall) { call(); + unlock(); + } } extern "C" void luacmain(lua_State * L, int stripping, lua_Chunkwriter w, void * uD); @@ -754,16 +917,80 @@ void Lua::dumpvars_r(Handle * h, int i, int depth) throw (GeneralException) { } } -Lua * Lua::thread() { - return new Lua(lua_newthread(L)); +Lua * Lua::thread(bool saveit) { + lua_State * L1 = lua_newthread(L); + Lua * Lt = find(L1); + if (saveit) { + push("BLUA_THREADS"); // -2 = thread, -1 = "BLUA_THREADS" + copy(); // -3 = thread, -2 = "BLUA_THREADS", -1 = "BLUA_THREADS" + gettable(LUA_REGISTRYINDEX); // -3 = thread, -2 = "BLUA_THREADS", -1 = BLUA_THREADS + push((lua_Number) (int) (void *) Lt); // -4 = thread, -3 = "BLUA_THREADS", -2 = BLUA_THREADS, -1 = key-Lt + copy(-4); // -5 = thread, -4 = "BLUA_THREADS", -3 = BLUA_THREADS, -2 = key-Lt, -1 = thread + settable(); // -3 = thread, -2 = "BLUA_THREADS", -1 = BLUA_THREADS + settable(LUA_REGISTRYINDEX); // -1 = thread + } + return Lt; } -int Lua::yield(int nargs) { - return lua_yield(L, nargs); +Lua * Lua::thread(const String & code, int nargs, bool saveit) { + Lua * L1; + L1 = thread(saveit); + L1->resume(code, nargs); + return L1; } -int Lua::resume(int nresults) { - return lua_resume(L, nresults); +Lua * Lua::thread(Handle * h, int nargs, bool saveit) { + Lua * L1; + L1 = thread(saveit); + L1->resume(h, nargs); + return L1; +} + +int Lua::yield(int nresults) { + return lua_yield(L, nresults); +} + +void Lua::resume(int nargs) throw (GeneralException) { + int r; + + lock(); + _protected = true; + r = lua_resume(L, nargs); + _protected = false; + + if (r != 0) { + push_lua_context(); + showerror(); + } + + unlock(); + + switch(r) { + case 0: + return; + case LUA_ERRRUN: + throw LuaException("Runtime error while running LUA code."); + case LUA_ERRMEM: + throw LuaException("Memory allocation error while running LUA code."); + case LUA_ERRERR: + throw LuaException("Error in Error function."); + default: + throw LuaException("Unknow error while running LUA code."); + } +} + +void Lua::resume(Handle * h, int nargs) { + lock(); + load(h, false); + resume(nargs); + unlock(); +} + +void Lua::resume(const String & s, int nargs) { + lock(); + load(s, false); + resume(nargs); + unlock(); } Lua * Lua::find(lua_State * __L) throw (GeneralException) { @@ -835,24 +1062,50 @@ void Lua::do_break() { lua_break(L); } +void Lua::lock() { +} + +void Lua::unlock() { +} + +void Lua::openlib(const String & libname, const luaL_reg *l, int nup) { + luaL_openlib(L, libname.to_charp(), l, nup); +} + +void Lua::setgcthreshold(int newthreshold) { + lua_setgcthreshold(L, newthreshold); +} + +int Lua::getgcthreshold() { + return lua_getgcthreshold(L); +} + +int Lua::getgccount() { + return lua_getgccount(L); +} + void LuaObject::push(Lua * L) throw (GeneralException) { if (pushed && wantdestruct) { throw GeneralException("Error: object is owned by the LUA script and can not be pushed."); } + L->lock(); L->newtable(); pushmembers(L); + L->unlock(); pushed = true; } void LuaObject::pushme(Lua * L, void * o, bool obj) { void ** u; bool * b; + L->lock(); L->push("__obj"); u = (void **) L->newuser(sizeof(o) + sizeof(bool)); *u = o; b = (bool *) (u + 1); *b = obj; L->settable(-3, true); + L->unlock(); } void * LuaObject::getme(Lua * L, int i) { @@ -876,12 +1129,15 @@ void * LuaObject::getme(Lua * L, int i) { } void LuaObject::pushit(Lua * L, const String & s, lua_CFunction f) { + L->lock(); L->push(s); L->push(f); L->settable(-3, true); + L->unlock(); } void LuaObject::pushmeta(Lua * L, const String & s, lua_CFunction f) { + L->lock(); if (!L->getmetatable()) { L->newtable(); } @@ -889,71 +1145,21 @@ void LuaObject::pushmeta(Lua * L, const String & s, lua_CFunction f) { L->push(f); L->settable(); L->setmetatable(); -} - -int LuaStatics::callwrap(lua_State * __L, lua_CFunction func) { - Lua * L = Lua::find(__L); - int n; - - try { - n = func(__L); - } - catch (LuaException e) { - L->error(String("LuaException: ") + e.GetMsg()); - } - catch (GeneralException e) { - L->error(String("GeneralException: ") + e.GetMsg()); - } - - return n; -} - -int LuaStatics::collector(lua_State * __L) { - Lua * L = Lua::find(__L); - void ** u = (void **) L->touserdata(); - bool * obj = (bool *) (u + 1); -// printm(M_INFO, "From LUA: collecting object\n"); - if (*obj) { -// printm(M_INFO, "Is object at %p\n", *u); - Base * b = (Base *) *u; - delete b; - } else { -// printm(M_INFO, "Is struct at %p\n", *u); - free(*u); - } - *u = 0; - return 0; -} - -int LuaStatics::destructor(lua_State * __L) { - Lua * L = Lua::find(__L); - Base * b = (Base *) LuaObject::getme(L); - delete b; - L->push("__obj"); - L->gettable(-2, true); - void ** u = (void **) L->touserdata(); - bool * obj = (bool *) (u + 1); - if (*obj) { - Base * b = (Base *) *u; - delete b; - } else { - free(*u); - } - *u = 0; - L->pop(); - return 0; + L->unlock(); } void LuaObject::pushdestruct(Lua * L) throw (GeneralException) { if (pushed) { throw GeneralException("Error: can't push destructor, object already pushed"); } + L->lock(); push(L); L->push("__obj"); L->gettable(-2, true); pushmeta(L, "__gc", LuaStatics::collector); L->pop(); pushit(L, "destroy", LuaStatics::destructor); + L->unlock(); wantdestruct = true; } -- cgit v1.2.3