From bc00e65c7a31d92e13e27c8ed1260e263d49360f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Tue, 17 Sep 2013 20:12:52 +0000 Subject: signal: initial signal handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Asbjørn Sloth Tønnesen Signed-off-by: Emil Renner Berthing --- Makefile.in | 2 + bin/lem.c | 4 +- lem/signal.lua | 107 ++++++++++++++++++++++++++++++ lem/signal/core.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 lem/signal.lua create mode 100644 lem/signal/core.c diff --git a/Makefile.in b/Makefile.in index a966c68..8b2405b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -29,6 +29,7 @@ llibs = \ lem/parsers.lua \ lem/io.lua \ lem/io/queue.lua \ + lem/signal.lua \ lem/lfs.lua \ lem/http.lua \ lem/http/response.lua \ @@ -41,6 +42,7 @@ clibs = \ lem/utils.so \ lem/parsers/core.so \ lem/io/core.so \ + lem/signal/core.so \ lem/lfs/core.so \ lem/http/core.so diff --git a/bin/lem.c b/bin/lem.c index 4da67e0..3e2b7a9 100644 --- a/bin/lem.c +++ b/bin/lem.c @@ -31,9 +31,9 @@ #include #if EV_USE_KQUEUE -#define LEM_LOOPFLAGS EVBACKEND_KQUEUE +#define LEM_LOOPFLAGS (EVFLAG_NOSIGMASK | EVBACKEND_KQUEUE) #else -#define LEM_LOOPFLAGS 0 +#define LEM_LOOPFLAGS EVFLAG_NOSIGMASK #endif #ifdef NDEBUG diff --git a/lem/signal.lua b/lem/signal.lua new file mode 100644 index 0000000..6dbf060 --- /dev/null +++ b/lem/signal.lua @@ -0,0 +1,107 @@ +-- +-- This file is part of LEM, a Lua Event Machine. +-- Copyright 2013 Asbjørn Sloth Tønnesen +-- +-- LEM is free software: you can redistribute it and/or modify it +-- under the terms of the GNU Lesser General Public License as +-- published by the Free Software Foundation, either version 3 of +-- the License, or (at your option) any later version. +-- +-- LEM is distributed in the hope that it will be useful, but +-- WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Lesser General Public License for more details. +-- +-- You should have received a copy of the GNU Lesser General Public +-- License along with LEM. If not, see . +-- + +local core = require 'lem.signal.core' + +local queues = {} + +local function fire(signal, ...) + local queue = queues[signal] + for i = 1, #queue do + queue[i](signal, ...) + end +end + +local install_signal_handler +do + local installed = false + function install_signal_handler() + if not installed then + core.sethandler(function(signal, ...) + fire(signal, ...) + end) + installed = true + end + end +end + +local signal_install, signal_uninstall +do + local installed = {} + + function signal_install(sig) + if installed[sig] then + return true + end + + install_signal_handler() + + local ret, err = core.watch(sig) + if not ret then return nil, err end + + installed[sig] = true + return true + end + + function signal_uninstall(sig) + if not installed[sig] then + return true + end + + local ret, err = core.unwatch(sig) + if not ret then return nil, err end + + installed[sig] = nil + return true + end +end + +local M = {} + +function M.register(signum, func) + if not signum then return nil, 'unknown signal' end + + local queue = queues[signum] + if queue == nil then + queue = {} + queues[signum] = queue + end + table.insert(queue, func) + return signal_install(signum) +end + +function M.unregister(signum, func) + if not signum then return nil, 'unknown signal' end + + local queue = queues[signum] + if queue then + for i = 1, #queue do + if queue[i] == func then + table.remove(queue, i) + end + end + if #queue == 0 then + return signal_uninstall(signum) + end + end + return true +end + +return M + +-- vim: ts=2 sw=2 noet: diff --git a/lem/signal/core.c b/lem/signal/core.c new file mode 100644 index 0000000..db6228a --- /dev/null +++ b/lem/signal/core.c @@ -0,0 +1,191 @@ +/* + * This file is part of LEM, a Lua Event Machine. + * Copyright 2013 Asbjørn Sloth Tønnesen + * Copyright 2013 Emil Renner Berthing + * + * LEM is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * LEM is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with LEM. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +static int signal_sethandler(lua_State *T); + +#if EV_SIGNAL_ENABLE +struct sigwatcher { + struct sigwatcher *next; + struct ev_signal w; +}; + +static sigset_t signal_sigset; +static struct sigwatcher *signal_watchers; + +static void +signal_os_handler(EV_P_ struct ev_signal *w, int revents) +{ + lua_State *S; + + (void)revents; + + S = lem_newthread(); + lua_pushlightuserdata(S, &signal_sethandler); + lua_rawget(S, LUA_REGISTRYINDEX); + if (lua_type(S, 1) != LUA_TFUNCTION) { + lem_forgetthread(S); + return; + } + + lua_pushinteger(S, w->signum); + lem_queue(S, 1); +} + +static int +signal_os_watch(lua_State *T, int sig) +{ + struct sigwatcher *s; + + if (sigismember(&signal_sigset, sig)) + goto out; /* already watched */ + + s = lem_xmalloc(sizeof(struct sigwatcher)); + + ev_signal_init(&s->w, signal_os_handler, sig); + ev_set_priority(&s->w, EV_MAXPRI); + ev_signal_start(LEM_ &s->w); + ev_unref(LEM); /* watcher shouldn't keep loop alive */ + + sigaddset(&signal_sigset, sig); + pthread_sigmask(SIG_UNBLOCK, &signal_sigset, NULL); + + s->next = signal_watchers; + signal_watchers = s; +out: + lua_pushboolean(T, 1); + return 1; +} + +static int +signal_os_unwatch(lua_State *T, int sig) +{ + struct sigwatcher **prevp; + struct sigwatcher *s; + + for (prevp = &signal_watchers, s = signal_watchers; + s != NULL; + prevp = &s->next, s = s->next) { + if (s->w.signum == sig) + break; + } + if (s != NULL) { + ev_ref(LEM); + ev_signal_stop(LEM_ &s->w); + + sigdelset(&signal_sigset, sig); + + *prevp = s->next; + free(s); + } + lua_pushboolean(T, 1); + return 1; +} +#else /* EV_SIGNAL_ENABLE */ +static int +signal_os_unsupported(lua_State *T) +{ + lua_pushnil(T); + lua_pushliteral(T, "Your libev is compiled without signal support."); + return 2 +} + +static inline int +signal_os_watch(lua_State *T) +{ + return signal_os_unsupported(T); +} + +static inline int +signal_os_unwatch(lua_State *T) +{ + return signal_os_unsupported(T); +} +#endif + +static int +signal_sethandler(lua_State *T) +{ + int type; + + if (lua_gettop(T) < 1) + lua_pushnil(T); + + type = lua_type(T, 1); + if (type != LUA_TNIL && type != LUA_TFUNCTION) + return luaL_argerror(T, 1, "expected nil or a function"); + + lua_settop(T, 1); + lua_pushlightuserdata(T, &signal_sethandler); + lua_insert(T, 1); + lua_rawset(T, LUA_REGISTRYINDEX); + return 0; +} + +static int +signal_watch(lua_State *T) +{ + int sig = luaL_checkint(T, 1); + + lua_settop(T, 1); + lua_pushlightuserdata(T, &signal_sethandler); + lua_rawget(T, LUA_REGISTRYINDEX); + if (lua_isnil(T, 2)) + return luaL_error(T, "You must set a signal handler first"); + + return signal_os_watch(T, sig); +} + +static int +signal_unwatch(lua_State *T) +{ + int sig = luaL_checkint(T, 1); + return signal_os_unwatch(T, sig); +} + +int +luaopen_lem_signal_core(lua_State *T) +{ +#if EV_SIGNAL_ENABLE + sigemptyset(&signal_sigset); + /* signal_watchers = NULL; globals are zero initalized */ +#endif + + /* create module table */ + lua_newtable(T); + + /* set sethandler function */ + lua_pushcfunction(T, signal_sethandler); + lua_setfield(T, -2, "sethandler"); + /* set watch function */ + lua_pushcfunction(T, signal_watch); + lua_setfield(T, -2, "watch"); + /* set unwatch function */ + lua_pushcfunction(T, signal_unwatch); + lua_setfield(T, -2, "unwatch"); + + return 1; +} -- cgit v1.2.1