209 lines
5.3 KiB
C
209 lines
5.3 KiB
C
/* 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;
|
|
}
|
|
|