aboutsummaryrefslogtreecommitdiffstats
path: root/src/sig-monitor.c
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2017-03-22 16:49:53 +0100
committerJosé Bollo <jose.bollo@iot.bzh>2017-03-22 16:49:53 +0100
commitfeccdb76f572a5fad947475c21b5b9aff696b04b (patch)
tree8e70e3eefef68febe02b5447105fdc6d857426b7 /src/sig-monitor.c
parentf5ce0df45f5ce9f0b57b250dfd44513d085f1e54 (diff)
Refactor of threading and signal monitor
The goal is to allow use of this facilities for things that are not 'afb_req'. Change-Id: I0d99c227934ed45136477bf6235bd1541d5f05cf Signed-off-by: José Bollo <jose.bollo@iot.bzh>
Diffstat (limited to 'src/sig-monitor.c')
-rw-r--r--src/sig-monitor.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/sig-monitor.c b/src/sig-monitor.c
new file mode 100644
index 00000000..d00f0f97
--- /dev/null
+++ b/src/sig-monitor.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <setjmp.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "sig-monitor.h"
+#include "verbose.h"
+
+/* local handler */
+static _Thread_local sigjmp_buf *error_handler;
+
+/* local timers */
+static _Thread_local int thread_timer_set;
+static _Thread_local timer_t thread_timerid;
+
+/*
+ * Creates a timer for the current thread
+ *
+ * Returns 0 in case of success
+ */
+static inline int timeout_create()
+{
+ int rc;
+ struct sigevent sevp;
+
+ if (thread_timer_set)
+ rc = 0;
+ else {
+ sevp.sigev_notify = SIGEV_THREAD_ID;
+ sevp.sigev_signo = SIGALRM;
+ sevp.sigev_value.sival_ptr = NULL;
+#if defined(sigev_notify_thread_id)
+ sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid);
+#else
+ sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid);
+#endif
+ rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
+ thread_timer_set = !rc;
+ }
+ return 0;
+}
+
+/*
+ * Arms the alarm in timeout seconds for the current thread
+ */
+static inline int timeout_arm(int timeout)
+{
+ int rc;
+ struct itimerspec its;
+
+ rc = timeout_create();
+ if (rc == 0) {
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = timeout;
+ its.it_value.tv_nsec = 0;
+ rc = timer_settime(thread_timerid, 0, &its, NULL);
+ }
+
+ return rc;
+}
+
+/*
+ * Disarms the current alarm
+ */
+static inline void timeout_disarm()
+{
+ if (thread_timer_set)
+ timeout_arm(0);
+}
+
+/*
+ * Destroy any alarm resource for the current thread
+ */
+static inline void timeout_delete()
+{
+ if (thread_timer_set) {
+ timer_delete(thread_timerid);
+ thread_timer_set = 0;
+ }
+}
+
+
+/* Handles signals that terminate the process */
+static void on_signal_terminate (int signum)
+{
+ ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
+ exit(1);
+}
+
+/* Handles monitored signals that can be continued */
+static void on_signal_error(int signum)
+{
+ sigset_t sigset;
+
+ // unlock signal to allow a new signal to come
+ if (error_handler != NULL) {
+ sigemptyset(&sigset);
+ sigaddset(&sigset, signum);
+ sigprocmask(SIG_UNBLOCK, &sigset, 0);
+ longjmp(*error_handler, signum);
+ }
+ if (signum == SIGALRM)
+ return;
+ ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
+ exit(2);
+}
+
+/* install the handlers */
+static int install(void (*handler)(int), int *signals)
+{
+ int result = 1;
+ while(*signals > 0) {
+ if (signal(*signals, handler) == SIG_ERR) {
+ ERROR("failed to install signal handler for signal %s", strsignal(*signals));
+ result = 0;
+ }
+ signals++;
+ }
+ return result;
+}
+
+int sig_monitor_init()
+{
+ static int sigerr[] = { SIGALRM, SIGSEGV, SIGFPE, 0 };
+ static int sigterm[] = { SIGINT, SIGABRT, 0 };
+
+ return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
+}
+
+int sig_monitor_init_timeouts()
+{
+ return timeout_create();
+}
+
+void sig_monitor_clean_timeouts()
+{
+ timeout_delete();
+}
+
+void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
+{
+ sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg, NULL, NULL);
+}
+
+void sig_monitor2(int timeout, void (*function)(int sig, void*, void*), void *arg1, void *arg2)
+{
+ sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg1, arg2, NULL);
+}
+
+void sig_monitor3(int timeout, void (*function)(int sig, void*, void*, void*), void *arg1, void *arg2, void *arg3)
+{
+ volatile int signum, signum2;
+ sigjmp_buf jmpbuf, *older;
+
+ older = error_handler;
+ signum = setjmp(jmpbuf);
+ if (signum == 0) {
+ error_handler = &jmpbuf;
+ if (timeout)
+ timeout_arm(timeout);
+ function(0, arg1, arg2, arg3);
+ } else {
+ signum2 = setjmp(jmpbuf);
+ if (signum2 == 0)
+ function(signum, arg1, arg2, arg3);
+ }
+ error_handler = older;
+ if (timeout)
+ timeout_disarm();
+}
+
+
+
+
+