summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAsbjørn Sloth Tønnesen <asbjorn@asbjorn.biz>2013-09-17 20:12:52 +0000
committerEmil Renner Berthing <esmil@mailme.dk>2013-11-11 20:43:46 +0100
commitbc00e65c7a31d92e13e27c8ed1260e263d49360f (patch)
treeb22dd38798a40a1deae37d1974e56bfda0ed4184
parent2d182370c5171bdeef3b5ad2b66235ea63ffbda8 (diff)
downloadlem-bc00e65c7a31d92e13e27c8ed1260e263d49360f.tar.gz
lem-bc00e65c7a31d92e13e27c8ed1260e263d49360f.tar.xz
lem-bc00e65c7a31d92e13e27c8ed1260e263d49360f.zip
signal: initial signal handling
Signed-off-by: Asbjørn Sloth Tønnesen <asbjorn@asbjorn.biz> Signed-off-by: Emil Renner Berthing <esmil@mailme.dk>
-rw-r--r--Makefile.in2
-rw-r--r--bin/lem.c4
-rw-r--r--lem/signal.lua107
-rw-r--r--lem/signal/core.c191
4 files changed, 302 insertions, 2 deletions
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 <lualib.h>
#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 <http://www.gnu.org/licenses/>.
+--
+
+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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <lem.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+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;
+}