Commit 347c4002 authored by Marek Vavruša's avatar Marek Vavruša

daemon/bindings: introduced 'event' package for timers

parent 7902ecad
......@@ -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/
......
......@@ -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;
}
......@@ -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
......@@ -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 = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment