]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/bindings: introduced 'event' package for timers
authorMarek Vavruša <marek.vavrusa@nic.cz>
Wed, 29 Apr 2015 14:13:23 +0000 (16:13 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Wed, 29 Apr 2015 14:13:23 +0000 (16:13 +0200)
daemon/README.rst
daemon/bindings.c
daemon/bindings.h
daemon/main.c

index 943b87c13e260367a47e4bb9ae840fbf9c6c5332..dff2e49417f1b93b58bd624d5def87c42d29f808 100644 (file)
@@ -153,20 +153,56 @@ to download cache from parent, to avoid cold-cache start.
 Events and services
 ^^^^^^^^^^^^^^^^^^^
 
-The Lua supports a concept called closures, this is extremely useful for scripting actions upon various events.
+The Lua supports a concept called closures_, this is extremely useful for scripting actions upon various events,
+say for example - prune the cache within minute after loading, publish statistics each 5 minutes and so on.
+Here's an example of an anonymous function with :func:`event.recurrent()`:
 
-.. note:: Work in progress, come back later!
+.. code-block:: lua
+
+       -- every 5 minutes
+       event.recurrent(5 * minute, function()
+               cachectl.prune()
+       end)
+
+Note that each scheduled event is identified by a number valid for the duration of the event,
+you may cancel it at any time. You can do this with anonymous functions, if you accept the event
+as a parameter, but it's not very useful as you don't have any *non-global* way to keep persistent variables.
+
+.. code-block:: lua
+
+       -- make a closure, encapsulating counter
+       function pruner()
+               local i = 0
+               -- pruning function
+               return function(e)
+                       cachectl.prune()
+                       -- cancel event on 5th attempt
+                       i = i + 1
+                       if i == 5 then
+                               event.cancel(e)
+                       fi
+               end
+       end
+
+       -- make recurrent event that will cancel after 5 times
+       event.recurrent(5 * minute, pruner())
 
-* Timers and events
 * File watchers
 * Data I/O
 
+.. note:: Work in progress, come back later!
+
+.. _closures: http://www.lua.org/pil/6.1.html
 
 Configuration reference
 -----------------------
 
 This is a reference for variables and functions available to both configuration file and CLI.
 
+.. contents::
+   :depth: 1
+   :local:
+
 Environment
 ^^^^^^^^^^^
 
@@ -319,6 +355,56 @@ daemons or manipulated from other processes, making for example synchronised loa
 
    Close the cache.
 
+
+Timers and events
+^^^^^^^^^^^^^^^^^
+
+The timer represents exactly the thing described in the examples - it allows you to execute closures after specified time,
+or event recurrent events. Time is always described in miliseconds, but there are convenient variables that you can use -
+``sec, minute, hour``. For example, ``5 * hour`` represents five hours, or 5*60*60*100 milliseconds.
+
+.. function:: event.after(time, function)
+
+   :return: event id
+
+   Execute function after the specified time has passed.
+   The first parameter of the callback is the event itself.
+
+   Example:
+
+   .. code-block:: lua
+
+       event.after(1 * minute, function() print('Hi!') end)
+
+.. function:: event.recurrent(interval, function)
+
+   :return: event id
+
+   Similar to :func:`event.after()`, periodically execute function after ``interval`` passes. 
+
+   Example:
+
+   .. code-block:: lua
+
+       msg_count = 0
+       event.recurrent(5 * sec, function(e) 
+               msg_count = msg_count + 1
+               print('Hi #'..msg_count)
+       end)
+
+.. function:: event.cancel(event_id)
+
+   Cancel running event, it has no effect on already canceled events.
+   New events may reuse the event_id, so the behaviour is undefined if the function
+   is called after another event is started.
+
+   Example:
+
+   .. code-block:: lua
+
+       e = event.after(1 * minute, function() print('Hi!') end)
+       event.cancel(e)
+
 .. _`JSON-encoded`: http://json.org/example
 .. _`Learn Lua in 15 minutes`: http://tylerneylon.com/a/learn-lua/
 .. _`PowerDNS Recursor`: https://doc.powerdns.com/md/recursor/scripting/
index dc677b3e4421786ebf9b11e17d4973043f568c40..d685d1fe7ad651d0272eba5ba4ab1ff39be4e9dd 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "lib/cache.h"
 #include "daemon/bindings.h"
+#include "daemon/worker.h"
 
 /** @internal Prefix error with file:line */
 static int format_error(lua_State* L, const char *err)
@@ -355,3 +356,119 @@ int lib_cache(lua_State *L)
        register_lib(L, "cache", lib);
        return 1;
 }
+
+static void event_free(uv_timer_t *timer)
+{
+       struct worker_ctx *worker = timer->loop->data;
+       lua_State *L = worker->engine->L;
+       int ref = (intptr_t) timer->data;
+       luaL_unref(L, LUA_REGISTRYINDEX, ref);
+       free(timer);
+}
+
+static void event_callback(uv_timer_t *timer)
+{
+       struct worker_ctx *worker = timer->loop->data;
+       lua_State *L = worker->engine->L;
+
+       /* Retrieve callback and execute */
+       lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) timer->data);
+       lua_rawgeti(L, -1, 1);
+       lua_pushinteger(L, (intptr_t) timer->data);
+       engine_pcall(L, 1);
+
+       /* Free callback if not recurrent */
+       if (uv_timer_get_repeat(timer) == 0) {
+               uv_close((uv_handle_t *)timer, (uv_close_cb) event_free);
+       }
+}
+
+static int event_sched(lua_State *L, unsigned timeout, unsigned repeat)
+{
+       uv_timer_t *timer = malloc(sizeof(*timer));
+       if (!timer) {
+               format_error(L, "out of memory");
+               lua_error(L);
+       }
+
+       /* Start timer with the reference */
+       uv_loop_t *loop = uv_default_loop();
+       uv_timer_init(loop, timer);
+       int ret = uv_timer_start(timer, event_callback, timeout, repeat);
+       if (ret != 0) {
+               free(timer);
+               format_error(L, "couldn't start the event");
+               lua_error(L);
+       }
+
+       /* Save callback and timer in registry */
+       lua_newtable(L);
+       lua_pushvalue(L, 2);
+       lua_rawseti(L, -2, 1);
+       lua_pushlightuserdata(L, timer);
+       lua_rawseti(L, -2, 2);
+       int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+       /* Save reference to the timer */
+       timer->data = (void *) (intptr_t)ref;
+       lua_pushinteger(L, ref);
+       return 1;
+}
+
+static int event_after(lua_State *L)
+{
+       /* Check parameters */
+       int n = lua_gettop(L);
+       if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) {
+               format_error(L, "expected 'after(number timeout, function)'");
+               lua_error(L);
+       }
+
+       return event_sched(L, lua_tonumber(L, 1), 0);
+}
+
+static int event_recurrent(lua_State *L)
+{
+       /* Check parameters */
+       int n = lua_gettop(L);
+       if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) {
+               format_error(L, "expected 'recurrent(number interval, function)'");
+               lua_error(L);
+       }
+       return event_sched(L, 0, lua_tonumber(L, 1));
+}
+
+static int event_cancel(lua_State *L)
+{
+       int n = lua_gettop(L);
+       if (n < 1 || !lua_isnumber(L, 1)) {
+               format_error(L, "expected 'cancel(number event)'");
+               lua_error(L);
+       }
+
+       /* Fetch event if it exists */
+       lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1));
+       if (!lua_istable(L, -1)) {
+               format_error(L, "event not exists");
+               lua_error(L);
+       }
+
+       /* Close the timer */
+       lua_rawgeti(L, -1, 2);
+       uv_handle_t *timer = lua_touserdata(L, -1);
+       uv_close(timer, (uv_close_cb) event_free);
+       return 0;
+}
+
+int lib_event(lua_State *L)
+{
+       static const luaL_Reg lib[] = {
+               { "after",      event_after },
+               { "recurrent",  event_recurrent },
+               { "cancel",     event_cancel },
+               { NULL, NULL }
+       };
+
+       register_lib(L, "event", lib);
+       return 1;
+}
index c1ad1a286aff3fc64c580becc4afd8e72bbc10e6..b6e0b1d1d6f13a789d2400021f23a8b13aa621c7 100644 (file)
@@ -44,4 +44,11 @@ int lib_net(lua_State *L);
  * @param  L scriptable
  * @return   number of packages to load
  */
-int lib_cache(lua_State *L);
\ No newline at end of file
+int lib_cache(lua_State *L);
+
+/**
+ * Load 'event' package.
+ * @param  L scriptable
+ * @return   number of packages to load
+ */
+int lib_event(lua_State *L);
\ No newline at end of file
index f3f4fe547d22a06adccabd28b095baf6be415f5f..24d45963f0eec37ad6936906674fcb6758a25ba5 100644 (file)
@@ -141,6 +141,7 @@ int main(int argc, char **argv)
        engine_lualib(&engine, "modules", lib_modules);
        engine_lualib(&engine, "net",     lib_net);
        engine_lualib(&engine, "cache",   lib_cache);
+       engine_lualib(&engine, "event",   lib_event);
 
        /* Create main worker. */
        struct worker_ctx worker = {