Mercurial > irccd
view libirccd-js/irccd/js/timer_js_api.cpp @ 773:8c44bbcbbab9
Misc: style, cleanup and update
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 26 Oct 2018 13:01:00 +0200 |
parents | 445c071e8efb |
children | 8460b4a34191 |
line wrap: on
line source
/* * timer_js_api.cpp -- Irccd.timer API * * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <boost/asio.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/logger.hpp> #include <irccd/daemon/plugin_service.hpp> #include "irccd_js_api.hpp" #include "js_plugin.hpp" #include "plugin_js_api.hpp" #include "timer_js_api.hpp" namespace asio = boost::asio; namespace irccd::js { namespace { const std::string_view signature("\xff""\xff""Irccd.Timer"); const std::string_view table("\xff""\xff""Irccd.Timer.callbacks"); // {{{ timer class timer : public std::enable_shared_from_this<timer> { public: enum class type { single, repeat }; private: boost::asio::deadline_timer handle_; js_plugin& plugin_; std::string key_; type type_; int delay_; bool is_running_{false}; bool is_waiting_{false}; void handle(); public: timer(boost::asio::io_service&, js_plugin&, type, int); auto key() const noexcept -> const std::string&; void start(); void stop(); }; void timer::handle() { duk::stack_guard sa(plugin_.get_context()); duk_push_global_stash(plugin_.get_context()); duk_get_prop_string(plugin_.get_context(), -1, table.data()); duk_remove(plugin_.get_context(), -2); duk_get_prop_string(plugin_.get_context(), -1, key_.c_str()); duk_remove(plugin_.get_context(), -2); if (duk_pcall(plugin_.get_context(), 0)) { auto& log = duk::type_traits<irccd>::self(plugin_.get_context()).get_log(); log.warning(static_cast<const plugin&>(plugin_)) << "timer error:" << std::endl; log.warning(static_cast<const plugin&>(plugin_)) << " " << duk::get_stack(plugin_.get_context(), -1).what() << std::endl; } else duk_pop(plugin_.get_context()); } timer::timer(boost::asio::io_service& service, js_plugin& plugin, type type, int delay) : handle_(service) , plugin_(plugin) , type_(type) , delay_(delay) { } auto timer::key() const noexcept -> const std::string& { return key_; } void timer::start() { if (is_waiting_) return; is_running_ = is_waiting_ = true; handle_.expires_from_now(boost::posix_time::milliseconds(delay_)); handle_.async_wait([this] (auto code) { is_waiting_ = false; if (code) { is_running_ = false; return; } handle(); if (is_running_ && type_ == type::repeat) start(); }); } void timer::stop() { if (is_running_) { handle_.cancel(); is_running_ = false; } } // }}} // {{{ self auto self(duk_context* ctx) -> timer* { duk::stack_guard sa(ctx); duk_push_this(ctx); duk_get_prop_string(ctx, -1, signature.data()); auto ptr = duk_to_pointer(ctx, -1); duk_pop_2(ctx); if (!ptr) duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object"); return static_cast<timer*>(ptr); } // }}} // {{{ Irccd.Timer.prototype.start /* * Method: Irccd.Timer.prototype.start() * -------------------------------------------------------- * * Start the timer. If the timer is already started the method is a no-op. */ auto Timer_prototype_start(duk_context* ctx) -> duk_ret_t { self(ctx)->start(); return 0; } // }}} // {{{ Irccd.Timer.prototype.stop /* * Method: Irccd.Timer.prototype.stop() * -------------------------------------------------------- * * Stop the timer. */ auto Timer_prototype_stop(duk_context* ctx) -> duk_ret_t { self(ctx)->stop(); return 0; } // }}} // {{{ Irccd.Timer [destructor] /* * Function: Irccd.Timer() [destructor] * ------------------------------------------------------------------ * * Deleter the timer. */ auto Timer_destructor(duk_context* ctx) -> duk_ret_t { duk::stack_guard sa(ctx); // Get timer from this. duk_get_prop_string(ctx, 0, signature.data()); auto ptr = static_cast<timer*>(duk_to_pointer(ctx, -1)); duk_pop(ctx); // Remove callback from timer table. duk_push_global_stash(ctx); duk_get_prop_string(ctx, -1, table.data()); duk_remove(ctx, -2); duk_del_prop_string(ctx, -1, ptr->key().c_str()); duk_pop(ctx); delete ptr; return 0; } // }}} // {{{ Irccd.Timer [constructor] /* * Function: Irccd.Timer(type, delay, callback) [constructor] * -------------------------------------------------------- * * Create a new timer object. * * Arguments: * - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat), * - delay, the interval in milliseconds, * - callback, the function to call. */ auto Timer_constructor(duk_context* ctx) -> duk_ret_t { if (!duk_is_constructor_call(ctx)) return 0; try { // Check parameters. const auto type = duk_require_int(ctx, 0); const auto delay = duk_require_int(ctx, 1); if (type < static_cast<int>(timer::type::single) || type > static_cast<int>(timer::type::repeat)) duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); if (delay < 0) duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); if (!duk_is_callable(ctx, 2)) duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); auto& plugin = duk::type_traits<js_plugin>::self(ctx); auto& daemon = duk::type_traits<irccd>::self(ctx); auto object = new timer(daemon.get_service(), plugin, static_cast<timer::type>(type), delay); duk_push_this(ctx); duk_push_pointer(ctx, object); duk_put_prop_string(ctx, -2, signature.data()); duk_push_c_function(ctx, Timer_destructor, 1); duk_set_finalizer(ctx, -2); duk_pop(ctx); // Store the function in a table to be called later. duk_push_global_stash(ctx); duk_get_prop_string(ctx, -1, table.data()); duk_remove(ctx, -2); duk_dup(ctx, 2); duk_put_prop_string(ctx, -2, object->key().c_str()); duk_pop(ctx); } catch (const std::exception& ex) { duk::raise(ctx, ex); } return 0; } // }}} const duk_function_list_entry methods[] = { { "start", Timer_prototype_start, 0 }, { "stop", Timer_prototype_stop, 0 }, { nullptr, nullptr, 0 } }; const duk_number_list_entry constants[] = { { "Single", static_cast<int>(timer::type::single) }, { "Repeat", static_cast<int>(timer::type::repeat) }, { nullptr, 0 } }; } // !namespace auto timer_js_api::get_name() const noexcept -> std::string_view { return "Irccd.Timer"; } void timer_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) { duk::stack_guard sa(plugin->get_context()); duk_get_global_string(plugin->get_context(), "Irccd"); duk_push_c_function(plugin->get_context(), Timer_constructor, 3); duk_put_number_list(plugin->get_context(), -1, constants); duk_push_object(plugin->get_context()); duk_put_function_list(plugin->get_context(), -1, methods); duk_put_prop_string(plugin->get_context(), -2, "prototype"); duk_put_prop_string(plugin->get_context(), -2, "Timer"); duk_pop(plugin->get_context()); duk_push_global_stash(plugin->get_context()); duk_push_object(plugin->get_context()); duk_put_prop_string(plugin->get_context(), -2, table.data()); duk_pop(plugin->get_context()); } } // !irccd::js