diff options
| author | Alexander Gavrilov | 2012-08-23 19:27:12 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-08-23 19:27:28 +0400 |
| commit | c6c5ad56c9b4421e1bf8a5bc2f38323734a9d2eb (patch) | |
| tree | d81ff429044aa2cfd70f901c615c64db48f5ad3e /library | |
| parent | 7046a6abbc157dd0102189e7dd6769183196766f (diff) | |
| download | dfhack-c6c5ad56c9b4421e1bf8a5bc2f38323734a9d2eb.tar.gz dfhack-c6c5ad56c9b4421e1bf8a5bc2f38323734a9d2eb.tar.bz2 dfhack-c6c5ad56c9b4421e1bf8a5bc2f38323734a9d2eb.tar.xz | |
Track lua event listener count, and let the C++ host know.
This allows completely avoiding the call overhead if there
are none. The downside is that the event object now has to
be a userdata with lots of metamethods.
Diffstat (limited to 'library')
| -rw-r--r-- | library/LuaTools.cpp | 174 | ||||
| -rw-r--r-- | library/PluginManager.cpp | 59 | ||||
| -rw-r--r-- | library/include/LuaTools.h | 37 | ||||
| -rw-r--r-- | library/include/PluginManager.h | 21 | ||||
| -rw-r--r-- | library/lua/dfhack.lua | 9 |
5 files changed, 246 insertions, 54 deletions
diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 75dfe23f..7c2c8f8d 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -68,6 +68,11 @@ lua_State *DFHack::Lua::Core::State = NULL; void dfhack_printerr(lua_State *S, const std::string &str); +inline bool is_null_userdata(lua_State *L, int idx) +{ + return lua_islightuserdata(L, idx) && !lua_touserdata(L, idx); +} + inline void AssertCoreSuspend(lua_State *state) { assert(!Lua::IsCoreContext(state) || DFHack::Core::getInstance().isSuspended()); @@ -1244,14 +1249,123 @@ static const luaL_Reg dfhack_coro_funcs[] = { static int DFHACK_EVENT_META_TOKEN = 0; -int DFHack::Lua::NewEvent(lua_State *state) +namespace { + struct EventObject { + int item_count; + Lua::Event::Owner *owner; + }; +} + +void DFHack::Lua::Event::New(lua_State *state, Owner *owner) { - lua_newtable(state); + auto obj = (EventObject *)lua_newuserdata(state, sizeof(EventObject)); + obj->item_count = 0; + obj->owner = owner; + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN); lua_setmetatable(state, -2); + lua_newtable(state); + lua_setuservalue(state, -2); +} + +void DFHack::Lua::Event::SetPrivateCallback(lua_State *L, int event) +{ + lua_getuservalue(L, event); + lua_swap(L); + lua_rawsetp(L, -2, NULL); + lua_pop(L, 1); +} + +static int dfhack_event_new(lua_State *L) +{ + Lua::Event::New(L); return 1; } +static int dfhack_event_len(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TUSERDATA); + auto obj = (EventObject *)lua_touserdata(L, 1); + lua_pushinteger(L, obj->item_count); + return 1; +} + +static int dfhack_event_tostring(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TUSERDATA); + auto obj = (EventObject *)lua_touserdata(L, 1); + lua_pushfstring(L, "<event: %d listeners>", obj->item_count); + return 1; +} + +static int dfhack_event_index(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_getuservalue(L, 1); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + return 1; +} + +static int dfhack_event_next(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_getuservalue(L, 1); + lua_pushvalue(L, 2); + while (lua_next(L, -2)) + { + if (is_null_userdata(L, -2)) + lua_pop(L, 1); + else + return 2; + } + lua_pushnil(L); + return 1; +} + +static int dfhack_event_pairs(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_pushcfunction(L, dfhack_event_next); + lua_pushvalue(L, 1); + lua_pushnil(L); + return 3; +} + +static int dfhack_event_newindex(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TUSERDATA); + if (is_null_userdata(L, 2)) + luaL_argerror(L, 2, "Key NULL is reserved in events."); + + lua_settop(L, 3); + lua_getuservalue(L, 1); + bool new_nil = lua_isnil(L, 3); + + lua_pushvalue(L, 2); + lua_rawget(L, 4); + bool old_nil = lua_isnil(L, -1); + lua_settop(L, 4); + + lua_pushvalue(L, 2); + lua_pushvalue(L, 3); + lua_rawset(L, 4); + + int delta = 0; + if (old_nil && !new_nil) delta = 1; + else if (new_nil && !old_nil) delta = -1; + + if (delta != 0) + { + auto obj = (EventObject *)lua_touserdata(L, 1); + obj->item_count += delta; + if (obj->owner) + obj->owner->on_count_changed(obj->item_count, delta); + } + + return 0; +} + static void do_invoke_event(lua_State *L, int argbase, int num_args, int errorfun) { for (int i = 0; i < num_args; i++) @@ -1292,7 +1406,7 @@ static void dfhack_event_invoke(lua_State *L, int base, bool from_c) while (lua_next(L, event)) { // Skip the NULL key in the main loop - if (lua_islightuserdata(L, -2) && !lua_touserdata(L, -2)) + if (is_null_userdata(L, -2)) lua_pop(L, 1); else do_invoke_event(L, argbase, num_args, errorfun); @@ -1303,14 +1417,20 @@ static void dfhack_event_invoke(lua_State *L, int base, bool from_c) static int dfhack_event_call(lua_State *state) { - luaL_checktype(state, 1, LUA_TTABLE); + luaL_checktype(state, 1, LUA_TUSERDATA); luaL_checkstack(state, lua_gettop(state)+2, "stack overflow in event dispatch"); + auto obj = (EventObject *)lua_touserdata(state, 1); + if (obj->owner) + obj->owner->on_invoked(state, lua_gettop(state)-1, false); + + lua_getuservalue(state, 1); + lua_replace(state, 1); dfhack_event_invoke(state, 0, false); return 0; } -void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args) +void DFHack::Lua::Event::Invoke(color_ostream &out, lua_State *state, void *key, int num_args) { AssertCoreSuspend(state); @@ -1325,7 +1445,7 @@ void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, i lua_rawgetp(state, LUA_REGISTRYINDEX, key); - if (!lua_istable(state, -1)) + if (!lua_isuserdata(state, -1)) { if (!lua_isnil(state, -1)) out.printerr("Invalid event object in Lua::InvokeEvent"); @@ -1333,22 +1453,29 @@ void DFHack::Lua::InvokeEvent(color_ostream &out, lua_State *state, void *key, i return; } + auto obj = (EventObject *)lua_touserdata(state, -1); lua_insert(state, base+1); + if (obj->owner) + obj->owner->on_invoked(state, num_args, true); + + lua_getuservalue(state, base+1); + lua_replace(state, base+1); + color_ostream *cur_out = Lua::GetOutput(state); set_dfhack_output(state, &out); dfhack_event_invoke(state, base, true); set_dfhack_output(state, cur_out); } -void DFHack::Lua::MakeEvent(lua_State *state, void *key) +void DFHack::Lua::Event::Make(lua_State *state, void *key, Owner *owner) { lua_rawgetp(state, LUA_REGISTRYINDEX, key); if (lua_isnil(state, -1)) { lua_pop(state, 1); - NewEvent(state); + New(state, owner); } lua_dup(state); @@ -1358,7 +1485,7 @@ void DFHack::Lua::MakeEvent(lua_State *state, void *key) void DFHack::Lua::Notification::invoke(color_ostream &out, int nargs) { assert(state); - InvokeEvent(out, state, key, nargs); + Event::Invoke(out, state, key, nargs); } void DFHack::Lua::Notification::bind(lua_State *state, void *key) @@ -1369,12 +1496,12 @@ void DFHack::Lua::Notification::bind(lua_State *state, void *key) void DFHack::Lua::Notification::bind(lua_State *state, const char *name) { - MakeEvent(state, this); + Event::Make(state, this); if (handler) { PushFunctionWrapper(state, 0, name, handler); - lua_rawsetp(state, -2, NULL); + Event::SetPrivateCallback(state, -2); } this->state = state; @@ -1435,11 +1562,26 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) lua_newtable(state); lua_pushcfunction(state, dfhack_event_call); lua_setfield(state, -2, "__call"); - lua_pushcfunction(state, Lua::NewEvent); - lua_setfield(state, -2, "new"); + lua_pushcfunction(state, dfhack_event_len); + lua_setfield(state, -2, "__len"); + lua_pushcfunction(state, dfhack_event_tostring); + lua_setfield(state, -2, "__tostring"); + lua_pushcfunction(state, dfhack_event_index); + lua_setfield(state, -2, "__index"); + lua_pushcfunction(state, dfhack_event_newindex); + lua_setfield(state, -2, "__newindex"); + lua_pushcfunction(state, dfhack_event_pairs); + lua_setfield(state, -2, "__pairs"); lua_dup(state); lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EVENT_META_TOKEN); - lua_setfield(state, -2, "event"); + + lua_newtable(state); + lua_pushcfunction(state, dfhack_event_new); + lua_setfield(state, -2, "new"); + lua_dup(state); + lua_setfield(state, -3, "__metatable"); + lua_setfield(state, -3, "event"); + lua_pop(state, 1); // Initialize the dfhack global luaL_setfuncs(state, dfhack_funcs, 0); @@ -1599,7 +1741,7 @@ void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) { } Lua::Push(State, code); - Lua::InvokeEvent(out, State, (void*)onStateChange, 1); + Lua::Event::Invoke(out, State, (void*)onStateChange, 1); } static void run_timers(color_ostream &out, lua_State *L, @@ -1653,7 +1795,7 @@ void DFHack::Lua::Core::Init(color_ostream &out) // Register events lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN); - MakeEvent(State, (void*)onStateChange); + Event::Make(State, (void*)onStateChange); lua_setfield(State, -2, "onStateChange"); lua_pushcfunction(State, dfhack_timeout); diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 668b2aec..3c015b2a 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -108,6 +108,50 @@ struct Plugin::RefAutoinc ~RefAutoinc(){ lock->lock_sub(); }; }; +struct Plugin::LuaCommand { + Plugin *owner; + std::string name; + int (*command)(lua_State *state); + + LuaCommand(Plugin *owner, std::string name) + : owner(owner), name(name), command(NULL) {} +}; + +struct Plugin::LuaFunction { + Plugin *owner; + std::string name; + function_identity_base *identity; + bool silent; + + LuaFunction(Plugin *owner, std::string name) + : owner(owner), name(name), identity(NULL), silent(false) {} +}; + +struct Plugin::LuaEvent : public Lua::Event::Owner { + LuaFunction handler; + Lua::Notification *event; + bool active; + int count; + + LuaEvent(Plugin *owner, std::string name) + : handler(owner,name), event(NULL), active(false), count(0) + { + handler.silent = true; + } + + void on_count_changed(int new_cnt, int delta) { + RefAutoinc lock(handler.owner->access); + count = new_cnt; + if (event) + event->on_count_changed(new_cnt, delta); + } + void on_invoked(lua_State *state, int nargs, bool from_c) { + RefAutoinc lock(handler.owner->access); + if (event) + event->on_invoked(state, nargs, from_c); + } +}; + Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm) { filename = filepath; @@ -439,7 +483,11 @@ void Plugin::index_lua(DFLibrary *lib) cmd->handler.identity = evlist->event->get_handler(); cmd->event = evlist->event; if (cmd->active) + { cmd->event->bind(Lua::Core::State, cmd); + if (cmd->count > 0) + cmd->event->on_count_changed(cmd->count, 0); + } } } } @@ -477,8 +525,13 @@ int Plugin::lua_fun_wrapper(lua_State *state) RefAutoinc lock(cmd->owner->access); if (!cmd->identity) + { + if (cmd->silent) + return 0; + luaL_error(state, "plugin function %s() has been unloaded", (cmd->owner->name+"."+cmd->name).c_str()); + } return LuaWrapper::method_wrapper_core(state, cmd->identity); } @@ -506,14 +559,14 @@ void Plugin::open_lua(lua_State *state, int table) { for (auto it = lua_events.begin(); it != lua_events.end(); ++it) { - Lua::MakeEvent(state, it->second); + Lua::Event::Make(state, it->second, it->second); push_function(state, &it->second->handler); - lua_rawsetp(state, -2, NULL); + Lua::Event::SetPrivateCallback(state, -2); it->second->active = true; if (it->second->event) - it->second->event->bind(state, it->second); + it->second->event->bind(Lua::Core::State, it->second); lua_setfield(state, table, it->first.c_str()); } diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 897e7032..6b1afb88 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -310,9 +310,18 @@ namespace DFHack {namespace Lua { DFHACK_EXPORT bool IsCoreContext(lua_State *state); - DFHACK_EXPORT int NewEvent(lua_State *state); - DFHACK_EXPORT void MakeEvent(lua_State *state, void *key); - DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args); + namespace Event { + struct DFHACK_EXPORT Owner { + virtual ~Owner() {} + virtual void on_count_changed(int new_cnt, int delta) {} + virtual void on_invoked(lua_State *state, int nargs, bool from_c) {} + }; + + DFHACK_EXPORT void New(lua_State *state, Owner *owner = NULL); + DFHACK_EXPORT void Make(lua_State *state, void *key, Owner *owner = NULL); + DFHACK_EXPORT void SetPrivateCallback(lua_State *state, int ev_idx); + DFHACK_EXPORT void Invoke(color_ostream &out, lua_State *state, void *key, int num_args); + } class StackUnwinder { lua_State *state; @@ -365,18 +374,24 @@ namespace DFHack {namespace Lua { } } - class DFHACK_EXPORT Notification { + class DFHACK_EXPORT Notification : public Event::Owner { lua_State *state; void *key; function_identity_base *handler; + int count; public: Notification(function_identity_base *handler = NULL) - : state(NULL), key(NULL), handler(handler) {} + : state(NULL), key(NULL), handler(handler), count(0) {} + int get_listener_count() { return count; } lua_State *get_state() { return state; } function_identity_base *get_handler() { return handler; } + lua_State *state_if_count() { return (count > 0) ? state : NULL; } + + void on_count_changed(int new_cnt, int) { count = new_cnt; } + void invoke(color_ostream &out, int nargs); void bind(lua_State *state, const char *name); @@ -388,7 +403,7 @@ namespace DFHack {namespace Lua { static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ void name(color_ostream &out) { \ handler(out); \ - if (name##_event.get_state()) { \ + if (name##_event.state_if_count()) { \ name##_event.invoke(out, 0); \ } \ } @@ -397,7 +412,7 @@ namespace DFHack {namespace Lua { static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ void name(color_ostream &out, arg_type1 arg1) { \ handler(out, arg1); \ - if (auto state = name##_event.get_state()) { \ + if (auto state = name##_event.state_if_count()) { \ DFHack::Lua::Push(state, arg1); \ name##_event.invoke(out, 1); \ } \ @@ -407,7 +422,7 @@ namespace DFHack {namespace Lua { static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2) { \ handler(out, arg1, arg2); \ - if (auto state = name##_event.get_state()) { \ + if (auto state = name##_event.state_if_count()) { \ DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg2); \ name##_event.invoke(out, 2); \ @@ -418,7 +433,7 @@ namespace DFHack {namespace Lua { static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3) { \ handler(out, arg1, arg2, arg3); \ - if (auto state = name##_event.get_state()) { \ + if (auto state = name##_event.state_if_count()) { \ DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg2); \ DFHack::Lua::Push(state, arg3); \ @@ -430,7 +445,7 @@ namespace DFHack {namespace Lua { static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4) { \ handler(out, arg1, arg2, arg3, arg4); \ - if (auto state = name##_event.get_state()) { \ + if (auto state = name##_event.state_if_count()) { \ DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg2); \ DFHack::Lua::Push(state, arg3); \ @@ -443,7 +458,7 @@ namespace DFHack {namespace Lua { static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4, arg_type5 arg5) { \ handler(out, arg1, arg2, arg3, arg4, arg5); \ - if (auto state = name##_event.get_state()) { \ + if (auto state = name##_event.state_if_count()) { \ DFHack::Lua::Push(state, arg1); \ DFHack::Lua::Push(state, arg2); \ DFHack::Lua::Push(state, arg3); \ diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 22171a15..25b05ad4 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -173,31 +173,16 @@ namespace DFHack PluginManager * parent; plugin_state state; - struct LuaCommand { - Plugin *owner; - std::string name; - int (*command)(lua_State *state); - LuaCommand(Plugin *owner, std::string name) : owner(owner), name(name) {} - }; + struct LuaCommand; std::map<std::string, LuaCommand*> lua_commands; static int lua_cmd_wrapper(lua_State *state); - struct LuaFunction { - Plugin *owner; - std::string name; - function_identity_base *identity; - LuaFunction(Plugin *owner, std::string name) : owner(owner), name(name) {} - }; + struct LuaFunction; std::map<std::string, LuaFunction*> lua_functions; static int lua_fun_wrapper(lua_State *state); void push_function(lua_State *state, LuaFunction *fn); - struct LuaEvent { - LuaFunction handler; - Lua::Notification *event; - bool active; - LuaEvent(Plugin *owner, std::string name) : handler(owner,name), active(false) {} - }; + struct LuaEvent; std::map<std::string, LuaEvent*> lua_events; void index_lua(DFLibrary *lib); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 5ebc9809..5699e8a2 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -122,8 +122,9 @@ end -- Misc functions function printall(table) - if type(table) == 'table' or df.isvalid(table) == 'ref' then - for k,v in pairs(table) do + local ok,f,t,k = pcall(pairs,table) + if ok then + for k,v in f,t,k do print(string.format("%-23s\t = %s",tostring(k),tostring(v))) end end @@ -177,10 +178,6 @@ end -- String conversions -function dfhack.event:__tostring() - return "<event>" -end - function dfhack.persistent:__tostring() return "<persistent "..self.entry_id..":"..self.key.."=\"" ..self.value.."\":"..table.concat(self.ints,",")..">" |
