Mercurial > irccd
changeset 135:6be066ad2329
Irccd: plugins do not own timer anymore, #411
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 18 May 2016 13:24:50 +0200 |
parents | dc7d6ba08122 |
children | 01df93b56dde |
files | lib/irccd/js.hpp lib/irccd/mod-irccd.cpp lib/irccd/mod-timer.cpp lib/irccd/plugin-js.cpp lib/irccd/plugin-js.hpp lib/irccd/plugin.hpp lib/irccd/service-plugin.cpp lib/irccd/service-plugin.hpp |
diffstat | 8 files changed, 121 insertions(+), 171 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/irccd/js.hpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/js.hpp Wed May 18 13:24:50 2016 +0200 @@ -821,6 +821,17 @@ } /** + * Wrapper for [duk_set_finalizer](http://duktape.org/api.html#duk_set_finalizer). + * + * \param ctx the context + * \param index the object index + */ +inline void setFinalizer(ContextPtr ctx, Index index) +{ + duk_set_finalizer(ctx, index); +} + +/** * Wrapper for [duk_set_prototype](http://duktape.org/api.html#duk_set_prototype). * * \param ctx the context
--- a/lib/irccd/mod-irccd.cpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/mod-irccd.cpp Wed May 18 13:24:50 2016 +0200 @@ -63,7 +63,7 @@ { } -void IrccdModule::load(Irccd &, JsPlugin &plugin) +void IrccdModule::load(Irccd &irccd, JsPlugin &plugin) { duk::StackAssert sa(plugin.context()); @@ -89,6 +89,9 @@ // Set Irccd as global. duk::putGlobal(plugin.context(), "Irccd"); + + // Store global instance. + duk::putGlobal(plugin.context(), "\xff""\xff""irccd", duk::RawPointer<Irccd>{&irccd}); } } // !irccd
--- a/lib/irccd/mod-timer.cpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/mod-timer.cpp Wed May 18 13:24:50 2016 +0200 @@ -16,13 +16,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <cassert> -#include <cstdint> +#include <format.h> +#include "irccd.hpp" +#include "logger.hpp" #include "mod-timer.hpp" #include "plugin-js.hpp" #include "timer.hpp" +using namespace fmt::literals; + namespace irccd { namespace duk { @@ -30,7 +33,7 @@ template <> class TypeTraits<Timer> { public: - static std::string name() + static std::string name() noexcept { return "\xff""\xff""Timer"; } @@ -45,12 +48,42 @@ namespace { +const std::string CallbackTable{"\xff""\xff""irccd-timer-callbacks"}; + +void handleSignal(std::weak_ptr<JsPlugin> ptr, std::string key) +{ + auto plugin = ptr.lock(); + + if (!plugin) { + return; + } + + auto irccd = duk::getGlobal<duk::RawPointer<Irccd>>(plugin->context(), "\xff""\xff""irccd"); + + irccd->post([plugin, key] (Irccd &) { + duk::StackAssert sa(plugin->context()); + + duk::getGlobal<void>(plugin->context(), CallbackTable); + duk::getProperty<void>(plugin->context(), -1, key); + duk::remove(plugin->context(), -2); + + if (duk::is<duk::Function>(plugin->context(), -1)) { + if (duk::pcall(plugin->context()) != 0) { + log::warning("plugin {}: {}"_format(plugin->name(), duk::error(plugin->context(), -1).stack)); + } + + duk::pop(plugin->context()); + } else { + duk::pop(plugin->context()); + } + }); +} + /* * Method: Timer.start() * -------------------------------------------------------- * - * Start the timer. If the timer is already started the method - * is a no-op. + * Start the timer. If the timer is already started the method is a no-op. */ duk::Ret start(duk::ContextPtr ctx) { @@ -98,24 +131,49 @@ */ duk::Ret constructor(duk::ContextPtr ctx) { - int type = duk::require<int>(ctx, 0); - int delay = duk::require<int>(ctx, 1); + // Check parameters. + auto type = duk::require<int>(ctx, 0); + auto delay = duk::require<int>(ctx, 1); + if (type < static_cast<int>(TimerType::Single) || type > static_cast<int>(TimerType::Repeat)) { + duk::raise(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); + } + if (delay < 0) { + duk::raise(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); + } if (!duk::is<duk::Function>(ctx, 2)) { - duk::raise(ctx, duk::TypeError("missing callback function")); + duk::raise(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); } + // Construct the timer in 'this'. auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay); + auto plugin = std::static_pointer_cast<JsPlugin>(duk::getGlobal<duk::RawPointer<Plugin>>(ctx, "\xff""\xff""plugin")->shared_from_this()); + auto hash = std::to_string(reinterpret_cast<std::uintptr_t>(timer.get())); + + timer->onSignal.connect(std::bind(handleSignal, std::weak_ptr<JsPlugin>(plugin), hash)); - /* Add this timer to the underlying plugin */ - duk::getGlobal<duk::RawPointer<JsPlugin>>(ctx, "\xff""\xff""plugin")->addTimer(timer); + // Set a finalizer that closes the timer. + duk::construct(ctx, duk::Shared<Timer>{timer}); + duk::push(ctx, duk::This()); + duk::putProperty(ctx, -1, "\xff""\xff""timer-key", hash); + duk::push(ctx, duk::Function{[] (duk::ContextPtr ctx) -> duk::Ret { + duk::StackAssert sa(ctx); - /* Construct object */ - duk::construct(ctx, duk::Shared<Timer>{timer}); + duk::get<duk::Shared<Timer>>(ctx, 0)->stop(); + duk::getGlobal<void>(ctx, CallbackTable); + duk::deleteProperty(ctx, -1, duk::getProperty<std::string>(ctx, 0, "\xff""\xff""timer-key")); + duk::pop(ctx); + log::debug("plugin: timer destroyed"); - /* Now store the JavaScript function to call */ + return 0; + }, 1}); + duk::setFinalizer(ctx, -2); + + // Save a callback function into the callback table. + duk::getGlobal<void>(ctx, CallbackTable); duk::dup(ctx, 2); - duk::putGlobal(ctx, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); + duk::putProperty(ctx, -2, hash); + duk::pop(ctx); return 0; } @@ -144,6 +202,7 @@ duk::putProperty(plugin.context(), -2, "prototype"); duk::putProperty(plugin.context(), -2, "Timer"); duk::pop(plugin.context()); + duk::putGlobal(plugin.context(), CallbackTable, duk::Object{}); } } // !irccd
--- a/lib/irccd/plugin-js.cpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/plugin-js.cpp Wed May 18 13:24:50 2016 +0200 @@ -123,51 +123,6 @@ { } -JsPlugin::~JsPlugin() -{ - for (auto &timer : m_timers) { - timer->stop(); - } -} - -void JsPlugin::addTimer(std::shared_ptr<Timer> timer) noexcept -{ - std::weak_ptr<Timer> ptr(timer); - - /* - * These signals are called from the Timer thread and are transmitted to irccd so that it can - * calls appropriate timer functions. - */ - timer->onSignal.connect([this, ptr] () { - auto timer = ptr.lock(); - - if (timer) { - onTimerSignal(move(timer)); - } - }); - timer->onEnd.connect([this, ptr] () { - auto timer = ptr.lock(); - - if (timer) { - onTimerEnd(move(timer)); - } - }); - - m_timers.insert(move(timer)); -} - -void JsPlugin::removeTimer(const std::shared_ptr<Timer> &timer) noexcept -{ - duk::StackAssert sa(m_context); - - /* Remove the JavaScript function */ - duk::push(m_context, duk::Null{}); - duk::putGlobal(m_context, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); - - /* Remove from list */ - m_timers.erase(timer); -} - void JsPlugin::onChannelMode(Irccd &, const std::shared_ptr<Server> &server, const std::string &origin, @@ -450,11 +405,15 @@ call("onTopic", 4); } -void JsPlugin::onUnload(Irccd &) +void JsPlugin::onUnload(Irccd &irccd) { duk::StackAssert sa(m_context); call("onUnload"); + + for (const auto &module : irccd.moduleService().modules()) { + module->unload(irccd, *this); + } } void JsPlugin::onWhois(Irccd &, const std::shared_ptr<Server> &server, const ServerWhois &whois)
--- a/lib/irccd/plugin-js.hpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/plugin-js.hpp Wed May 18 13:24:50 2016 +0200 @@ -24,8 +24,6 @@ * \brief JavaScript plugins for irccd. */ -#include <unordered_set> - #include "js.hpp" #include "path.hpp" #include "plugin.hpp" @@ -34,49 +32,15 @@ namespace irccd { class Module; -class Timer; - -/** - * \brief Timers that a plugin owns. - */ -using PluginTimers = std::unordered_set<std::shared_ptr<Timer>>; /** * \brief JavaScript plugins for irccd. */ class JsPlugin : public Plugin { -public: - // TODO: remove with future modules - - /** - * Signal: onTimerSignal - * ------------------------------------------------ - * - * When a timer expires. - * - * Arguments: - * - the timer object - */ - Signal<std::shared_ptr<Timer>> onTimerSignal; - - /** - * Signal: onTimerEnd - * ------------------------------------------------ - * - * When a timer is finished. - * - * Arguments: - * - the timer object - */ - Signal<std::shared_ptr<Timer>> onTimerEnd; - private: // JavaScript context duk::Context m_context; - // Plugin info and its timers - PluginTimers m_timers; - // Store loaded modules. std::vector<std::shared_ptr<Module>> m_modules; @@ -97,24 +61,10 @@ */ JsPlugin(std::string name, std::string path, const PluginConfig &config = PluginConfig()); - /** - * Close timers. - */ - ~JsPlugin(); - - /** - * Add a timer to the plugin. - * - * \param timer the timer to add - */ - void addTimer(std::shared_ptr<Timer> timer) noexcept; - - /** - * Remove a timer from a plugin. - * - * \param timer - */ - void removeTimer(const std::shared_ptr<Timer> &timer) noexcept; + ~JsPlugin() + { + puts("~JsPlugin"); + } /** * Access the Duktape context.
--- a/lib/irccd/plugin.hpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/plugin.hpp Wed May 18 13:24:50 2016 +0200 @@ -49,7 +49,7 @@ * A plugin is identified by name and can be loaded and unloaded * at runtime. */ -class Plugin { +class Plugin : public std::enable_shared_from_this<Plugin> { private: // Plugin information std::string m_name; @@ -185,11 +185,21 @@ m_version = std::move(version); } + /** + * Access the plugin configuration. + * + * \return the config + */ inline const PluginConfig &config() const noexcept { return m_config; } + /** + * Overloaded function. + * + * \return the config + */ inline PluginConfig &config() noexcept { return m_config;
--- a/lib/irccd/service-plugin.cpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/service-plugin.cpp Wed May 18 13:24:50 2016 +0200 @@ -22,11 +22,9 @@ #include <format.h> -#include "config.hpp" #include "fs.hpp" #include "irccd.hpp" #include "logger.hpp" -#include "plugin.hpp" #include "plugin-js.hpp" #include "service-plugin.hpp" @@ -39,6 +37,13 @@ { } +PluginService::~PluginService() +{ + for (const auto &plugin : m_plugins) { + plugin->onUnload(m_irccd); + } +} + bool PluginService::has(const std::string &name) const noexcept { return std::count_if(m_plugins.cbegin(), m_plugins.cend(), [&] (const auto &plugin) { @@ -72,20 +77,7 @@ void PluginService::add(std::shared_ptr<Plugin> plugin) { - using namespace std::placeholders; - - // TODO: REMOVE WHEN WE GET THE JAVASCRIPT MODULES - std::shared_ptr<JsPlugin> jsp = std::dynamic_pointer_cast<JsPlugin>(plugin); - - if (jsp) { - std::weak_ptr<JsPlugin> ptr(jsp); - - jsp->onTimerSignal.connect(std::bind(&PluginService::handleTimerSignal, this, ptr, _1)); - jsp->onTimerEnd.connect(std::bind(&PluginService::handleTimerEnd, this, ptr, _1)); - - // Store reference to irccd. - duk::putGlobal(jsp->context(), "\xff""\xff""irccd", duk::RawPointer<Irccd>{&m_irccd}); - } + m_plugins.push_back(std::move(plugin)); } std::shared_ptr<Plugin> PluginService::find(std::string name) @@ -171,39 +163,4 @@ } } -void PluginService::handleTimerSignal(std::weak_ptr<JsPlugin> ptr, std::shared_ptr<Timer> timer) -{ - m_irccd.post([this, ptr, timer] (Irccd &) { - auto plugin = ptr.lock(); - - if (!plugin) { - return; - } - - auto &ctx = plugin->context(); - - duk::StackAssert sa(ctx); - - // TODO: improve this - try { - duk::getGlobal<void>(ctx, "\xff""\xff""timer-" + std::to_string(reinterpret_cast<std::intptr_t>(timer.get()))); - duk::pcall(ctx, 0); - duk::pop(ctx); - } catch (const std::exception &) { - } - }); -} - -void PluginService::handleTimerEnd(std::weak_ptr<JsPlugin> ptr, std::shared_ptr<Timer> timer) -{ - m_irccd.post([this, ptr, timer] (Irccd &) { - auto plugin = ptr.lock(); - - if (plugin) { - log::debug() << "timer: finished, removing from plugin `" << plugin->name() << "'" << std::endl; - plugin->removeTimer(timer); - } - }); -} - } // !irccd
--- a/lib/irccd/service-plugin.hpp Sun May 15 21:36:04 2016 +0200 +++ b/lib/irccd/service-plugin.hpp Wed May 18 13:24:50 2016 +0200 @@ -43,10 +43,6 @@ std::vector<std::shared_ptr<Plugin>> m_plugins; std::unordered_map<std::string, PluginConfig> m_config; - // TODO: get rid of this with future JavaScript modules. - void handleTimerSignal(std::weak_ptr<JsPlugin>, std::shared_ptr<Timer>); - void handleTimerEnd(std::weak_ptr<JsPlugin>, std::shared_ptr<Timer>); - public: /** * Create the plugin service. @@ -56,6 +52,11 @@ PluginService(Irccd &irccd) noexcept; /** + * Destroy plugins. + */ + ~PluginService(); + + /** * Get the list of plugins. * * \return the list of plugins