diff options
Diffstat (limited to '')
-rw-r--r-- | daemon/bindings/event.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/daemon/bindings/event.c b/daemon/bindings/event.c new file mode 100644 index 0000000..4cefa13 --- /dev/null +++ b/daemon/bindings/event.c @@ -0,0 +1,209 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "daemon/bindings/impl.h" + +#include <unistd.h> +#include <uv.h> + +static void event_free(uv_timer_t *timer) +{ + lua_State *L = the_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) +{ + lua_State *L = the_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); + int ret = execute_callback(L, 1); + /* Free callback if not recurrent or an error */ + if (ret != 0 || (uv_timer_get_repeat(timer) == 0 && uv_is_active((uv_handle_t *)timer) == 0)) { + if (!uv_is_closing((uv_handle_t *)timer)) { + uv_close((uv_handle_t *)timer, (uv_close_cb) event_free); + } + } +} + +static void event_fdcallback(uv_poll_t* handle, int status, int events) +{ + lua_State *L = the_worker->engine->L; + + /* Retrieve callback and execute */ + lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) handle->data); + lua_rawgeti(L, -1, 1); + lua_pushinteger(L, (intptr_t) handle->data); + lua_pushinteger(L, status); + lua_pushinteger(L, events); + int ret = execute_callback(L, 3); + /* Free callback if not recurrent or an error */ + if (ret != 0) { + if (!uv_is_closing((uv_handle_t *)handle)) { + uv_close((uv_handle_t *)handle, (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) + lua_error_p(L, "out of memory"); + + /* 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); + lua_error_p(L, "couldn't start the event"); + } + + /* Save callback and timer in registry */ + lua_newtable(L); + lua_pushvalue(L, 2); + lua_rawseti(L, -2, 1); + lua_pushpointer(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)) + lua_error_p(L, "expected 'after(number timeout, function)'"); + + return event_sched(L, lua_tointeger(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_tointeger(L, 1) == 0 + || !lua_isfunction(L, 2)) + lua_error_p(L, "expected 'recurrent(number interval, function)'"); + + return event_sched(L, 0, lua_tointeger(L, 1)); +} + +static int event_cancel(lua_State *L) +{ + int n = lua_gettop(L); + if (n < 1 || !lua_isnumber(L, 1)) + lua_error_p(L, "expected 'cancel(number event)'"); + + /* Fetch event if it exists */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1)); + bool ok = lua_istable(L, -1); + + /* Close the timer */ + uv_handle_t **timer_pp = NULL; + if (ok) { + lua_rawgeti(L, -1, 2); + timer_pp = lua_touserdata(L, -1); + ok = timer_pp && *timer_pp; + /* That have been sufficient safety checks, hopefully. */ + } + if (ok && !uv_is_closing(*timer_pp)) { + uv_close(*timer_pp, (uv_close_cb)event_free); + } + lua_pushboolean(L, ok); + return 1; +} + +static int event_reschedule(lua_State *L) +{ + int n = lua_gettop(L); + if (n < 2 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2)) + lua_error_p(L, "expected 'reschedule(number event, number timeout)'"); + + /* Fetch event if it exists */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1)); + bool ok = lua_istable(L, -1); + + /* Reschedule the timer */ + uv_handle_t **timer_pp = NULL; + if (ok) { + lua_rawgeti(L, -1, 2); + timer_pp = lua_touserdata(L, -1); + ok = timer_pp && *timer_pp; + /* That have been sufficient safety checks, hopefully. */ + } + if (ok && !uv_is_closing(*timer_pp)) { + int ret = uv_timer_start((uv_timer_t *)*timer_pp, + event_callback, lua_tointeger(L, 2), 0); + if (ret != 0) { + uv_close(*timer_pp, (uv_close_cb)event_free); + ok = false; + } + } + lua_pushboolean(L, ok); + return 1; +} + +static int event_fdwatch(lua_State *L) +{ + /* Check parameters */ + int n = lua_gettop(L); + if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) + lua_error_p(L, "expected 'socket(number fd, function)'"); + + uv_poll_t *handle = malloc(sizeof(*handle)); + if (!handle) + lua_error_p(L, "out of memory"); + + /* Start timer with the reference */ + int sock = lua_tointeger(L, 1); + uv_loop_t *loop = uv_default_loop(); + int ret = uv_poll_init(loop, handle, sock); + if (ret == 0) + ret = uv_poll_start(handle, UV_READABLE, event_fdcallback); + if (ret != 0) { + free(handle); + lua_error_p(L, "couldn't start event poller"); + } + + /* Save callback and timer in registry */ + lua_newtable(L); + lua_pushvalue(L, 2); + lua_rawseti(L, -2, 1); + lua_pushpointer(L, handle); + lua_rawseti(L, -2, 2); + int ref = luaL_ref(L, LUA_REGISTRYINDEX); + + /* Save reference to the timer */ + handle->data = (void *) (intptr_t)ref; + lua_pushinteger(L, ref); + return 1; +} + +int kr_bindings_event(lua_State *L) +{ + static const luaL_Reg lib[] = { + { "after", event_after }, + { "recurrent", event_recurrent }, + { "cancel", event_cancel }, + { "socket", event_fdwatch }, + { "reschedule", event_reschedule }, + { NULL, NULL } + }; + + luaL_register(L, "event", lib); + return 1; +} + |