diff options
| author | Alexander Gavrilov | 2012-04-14 19:44:07 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-04-14 19:44:07 +0400 |
| commit | cb49c92b99a5aab3f95c811a43fe4dadc1115b99 (patch) | |
| tree | fe2a4bcfc0a11e5ef3e2cbfbc864e5b9e8aa9ceb /library/PluginManager.cpp | |
| parent | 7a34a89f53071a8368ec5bd064accb10354b6d56 (diff) | |
| download | dfhack-cb49c92b99a5aab3f95c811a43fe4dadc1115b99.tar.gz dfhack-cb49c92b99a5aab3f95c811a43fe4dadc1115b99.tar.bz2 dfhack-cb49c92b99a5aab3f95c811a43fe4dadc1115b99.tar.xz | |
Allow plugins to export functions to lua with safe reload support.
- To ensure reload safety functions have to be wrapped. Every call
checks the loaded state and locks a mutex in Plugin. If the plugin
is unloaded, calling its functions throws a lua error. Therefore,
plugins may not create closures or export yieldable functions.
- The set of function argument and return types supported by
LuaWrapper is severely limited when compared to being compiled
inside the main library.
Currently supported types: numbers, bool, std::string, df::foo,
df::foo*, std::vector<bool>, std::vector<df::foo*>.
- To facilitate postponing initialization until after all plugins
have been loaded, the core sends a SC_CORE_INITIALIZED event.
- As an example, the burrows plugin now exports its functions.
Diffstat (limited to 'library/PluginManager.cpp')
| -rw-r--r-- | library/PluginManager.cpp | 112 |
1 files changed, 108 insertions, 4 deletions
diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index a7faabee..4d0c06dd 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -32,6 +32,8 @@ distribution. #include "DataDefs.h" #include "MiscUtils.h" +#include "LuaWrapper.h" + using namespace DFHack; #include <string> @@ -107,8 +109,8 @@ struct Plugin::RefLock void lock_sub() { mut->lock(); - refcount --; - wakeup->notify_one(); + if (--refcount == 0) + wakeup->notify_one(); mut->unlock(); } void wait() @@ -130,6 +132,13 @@ struct Plugin::RefAutolock ~RefAutolock(){ lock->unlock(); }; }; +struct Plugin::RefAutoinc +{ + RefLock * lock; + RefAutoinc(RefLock * lck):lock(lck){ lock->lock_add(); }; + ~RefAutoinc(){ lock->lock_sub(); }; +}; + Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _filename, PluginManager * pm) { filename = filepath; @@ -210,6 +219,7 @@ bool Plugin::load(color_ostream &con) plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown"); plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange"); plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect"); + index_lua(plug); this->name = *plug_name; plugin_lib = plug; commands.clear(); @@ -222,6 +232,7 @@ bool Plugin::load(color_ostream &con) else { con.printerr("Plugin %s has failed to initialize properly.\n", filename.c_str()); + reset_lua(); ClosePlugin(plugin_lib); state = PS_BROKEN; return false; @@ -235,13 +246,22 @@ bool Plugin::unload(color_ostream &con) // if we are actually loaded if(state == PS_LOADED) { + // notify the plugin about an attempt to shutdown + if (plugin_onstatechange && + plugin_onstatechange(con, SC_BEGIN_UNLOAD) == CR_NOT_FOUND) + { + con.printerr("Plugin %s has refused to be unloaded.\n", name.c_str()); + access->unlock(); + return false; + } + // wait for all calls to finish + access->wait(); // notify plugin about shutdown, if it has a shutdown function command_result cr = CR_OK; if(plugin_shutdown) cr = plugin_shutdown(con); - // wait for all calls to finish - access->wait(); // cleanup... + reset_lua(); parent->unregisterCommands(this); commands.clear(); if(cr == CR_OK) @@ -418,6 +438,90 @@ Plugin::plugin_state Plugin::getState() const return state; } +void Plugin::index_lua(DFLibrary *lib) +{ + if (auto cmdlist = (CommandReg*)LookupPlugin(lib, "plugin_lua_commands")) + { + for (; cmdlist->name; ++cmdlist) + { + auto &cmd = lua_commands[cmdlist->name]; + if (!cmd) cmd = new LuaCommand; + cmd->owner = this; + cmd->name = cmdlist->name; + cmd->command = cmdlist->command; + } + } + if (auto funlist = (FunctionReg*)LookupPlugin(lib, "plugin_lua_functions")) + { + for (; funlist->name; ++funlist) + { + auto &cmd = lua_functions[funlist->name]; + if (!cmd) cmd = new LuaFunction; + cmd->owner = this; + cmd->name = funlist->name; + cmd->identity = funlist->identity; + } + } +} + +void Plugin::reset_lua() +{ + for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it) + it->second->command = NULL; + for (auto it = lua_functions.begin(); it != lua_functions.end(); ++it) + it->second->identity = NULL; +} + +int Plugin::lua_cmd_wrapper(lua_State *state) +{ + auto cmd = (LuaCommand*)lua_touserdata(state, lua_upvalueindex(1)); + + RefAutoinc lock(cmd->owner->access); + + if (!cmd->command) + luaL_error(state, "plugin command %s() has been unloaded", + (cmd->owner->name+"."+cmd->name).c_str()); + + return cmd->command(state); +} + +int Plugin::lua_fun_wrapper(lua_State *state) +{ + auto cmd = (LuaFunction*)lua_touserdata(state, UPVAL_CONTAINER_ID); + + RefAutoinc lock(cmd->owner->access); + + if (!cmd->identity) + luaL_error(state, "plugin function %s() has been unloaded", + (cmd->owner->name+"."+cmd->name).c_str()); + + return LuaWrapper::method_wrapper_core(state, cmd->identity); +} + +void Plugin::open_lua(lua_State *state, int table) +{ + table = lua_absindex(state, table); + + RefAutolock lock(access); + + for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it) + { + lua_pushlightuserdata(state, it->second); + lua_pushcclosure(state, lua_cmd_wrapper, 1); + lua_setfield(state, table, it->first.c_str()); + } + + for (auto it = lua_functions.begin(); it != lua_functions.end(); ++it) + { + lua_rawgetp(state, LUA_REGISTRYINDEX, &LuaWrapper::DFHACK_TYPETABLE_TOKEN); + lua_pushlightuserdata(state, NULL); + lua_pushfstring(state, "%s.%s()", name.c_str(), it->second->name.c_str()); + lua_pushlightuserdata(state, it->second); + lua_pushcclosure(state, lua_fun_wrapper, 4); + lua_setfield(state, table, it->first.c_str()); + } +} + PluginManager::PluginManager(Core * core) { #ifdef LINUX_BUILD |
