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
^^^^^^^^^^^
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/
#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)
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;
+}