summaryrefslogtreecommitdiffstats
path: root/daemon/bindings/event.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--daemon/bindings/event.c209
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;
+}
+