]> git.decadent.org.uk Git - ion3.git/blobdiff - libmainloop/signal.c
Imported Upstream version 20090110
[ion3.git] / libmainloop / signal.c
index ddf6aba40cf582115bb133596c4b2b0aa39072de..c5365532dd1b56d537d9150887df06a6b4385307 100644 (file)
@@ -1,12 +1,9 @@
 /*
  * ion/libmainloop/signal.c
  *
- * Copyright (c) Tuomo Valkonen 1999-2006
+ * Copyright (c) Tuomo Valkonen 1999-2009
  *
- * Ion 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 2.1 of the License, or
- * (at your option) any later version.
+ * See the included file LICENSE for details.
  */
 
 #include <unistd.h>
@@ -17,6 +14,7 @@
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
+#include <errno.h>
 
 #include <libtu/objp.h>
 #include <libtu/types.h>
@@ -38,6 +36,8 @@ static bool had_tmr=FALSE;
 WHook *mainloop_sigchld_hook=NULL;
 WHook *mainloop_sigusr2_hook=NULL;
 
+static sigset_t special_sigs;
+
 
 /*{{{ Timers */
 
@@ -45,6 +45,34 @@ WHook *mainloop_sigusr2_hook=NULL;
 static WTimer *queue=NULL;
 
 
+int mainloop_gettime(struct timeval *val)
+{
+#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK>=0)
+    struct timespec spec;
+    int ret;
+    static int checked=0;
+    
+    if(checked>=0){
+        ret=clock_gettime(CLOCK_MONOTONIC, &spec);
+    
+        if(ret==-1 && errno==EINVAL && checked==0){
+            checked=-1;
+        }else{
+            checked=1;
+            
+            val->tv_sec=spec.tv_sec;
+            val->tv_usec=spec.tv_nsec/1000;
+        
+            return ret;
+        }
+    }
+#else
+    #warning "Monotonic clock unavailable; please fix your operating system."
+#endif
+    return gettimeofday(val, NULL);
+}
+
+
 #define TIMEVAL_LATER(a, b) \
     ((a.tv_sec > b.tv_sec) || \
     ((a.tv_sec == b.tv_sec) && \
@@ -53,44 +81,51 @@ static WTimer *queue=NULL;
 #define USECS_IN_SEC 1000000
 
 
-static void do_timer_set()
+bool libmainloop_get_timeout(struct timeval *tv)
 {
-    struct itimerval val={{0, 0}, {0, 0}};
-    
-    if(queue==NULL){
-        setitimer(ITIMER_REAL, &val, NULL);
-        return;
-    }
+    if(queue==NULL)
+        return FALSE;
 
     /* Subtract queue time from current time, don't go below zero */
-    gettimeofday(&(val.it_value), NULL);
-    if(TIMEVAL_LATER((queue)->when, val.it_value)){
-        if(queue->when.tv_usec<val.it_value.tv_usec){
-            queue->when.tv_usec+=USECS_IN_SEC;
-            queue->when.tv_sec--;
+    mainloop_gettime(tv);
+    if(TIMEVAL_LATER((queue)->when, (*tv))){
+        if(queue->when.tv_usec<tv->tv_usec){
+            tv->tv_usec=(queue->when.tv_usec+USECS_IN_SEC)-tv->tv_usec;
+            /* TIMEVAL_LATER ensures >= 0 */
+            tv->tv_sec=(queue->when.tv_sec-1)-tv->tv_sec;
+        }else{
+            tv->tv_usec=queue->when.tv_usec-tv->tv_usec;
+            tv->tv_sec=queue->when.tv_sec-tv->tv_sec;
         }
-        val.it_value.tv_usec=queue->when.tv_usec-val.it_value.tv_usec;
-        val.it_value.tv_sec=queue->when.tv_sec-val.it_value.tv_sec;
-        if(val.it_value.tv_usec<0)
-            val.it_value.tv_usec=0;
         /* POSIX and some kernels have been designed by absolute morons and 
          * contain idiotic artificial restrictions on the value of tv_usec, 
          * that will only cause more code being run and clock cycles being 
          * spent to do the same thing, as the kernel will in any case convert
          * the seconds to some other units.
          */
-         val.it_value.tv_sec+=val.it_value.tv_usec/USECS_IN_SEC;
-         val.it_value.tv_usec%=USECS_IN_SEC;
+         tv->tv_sec+=tv->tv_usec/USECS_IN_SEC;
+         tv->tv_usec%=USECS_IN_SEC;
     }else{
         had_tmr=TRUE;
-        return;
+        return FALSE;
     }
+    
+    return TRUE;
+}
+
 
-    val.it_interval.tv_usec=val.it_value.tv_usec;
-    val.it_interval.tv_sec=val.it_value.tv_sec;
+static void do_timer_set()
+{
+    struct itimerval val={{0, 0}, {0, 0}};
     
-    if((setitimer(ITIMER_REAL, &val, NULL))){
-        had_tmr=TRUE;
+    if(libmainloop_get_timeout(&val.it_value)){
+        val.it_interval.tv_usec=0;
+        val.it_interval.tv_sec=0;
+        
+        if((setitimer(ITIMER_REAL, &val, NULL)))
+            had_tmr=TRUE;
+    }else if(!had_tmr){
+        setitimer(ITIMER_REAL, &val, NULL);
     }
 }
 
@@ -191,9 +226,11 @@ bool mainloop_check_signals()
         return kill_sig;
 
     /* Check for timer events in the queue */
-    while(had_tmr && queue!=NULL){
+    while(had_tmr){
         had_tmr=FALSE;
-        gettimeofday(&current_time, NULL);
+        if(queue==NULL)
+            break;
+        mainloop_gettime(&current_time);
         while(queue!=NULL){
             if(TIMEVAL_LATER(current_time, queue->when)){
                 q=queue;
@@ -224,11 +261,23 @@ bool mainloop_check_signals()
 }
 
 
+void mainloop_block_signals(sigset_t *oldmask)
+{
+    sigprocmask(SIG_BLOCK, &special_sigs, oldmask);
+}
+
+
+bool mainloop_unhandled_signals()
+{
+    return (usr2_sig || wait_sig || kill_sig || had_tmr);
+}
+
+
 static void add_to_current_time(struct timeval *when, uint msecs)
 {
     long tmp_usec;
 
-    gettimeofday(when, NULL);
+    mainloop_gettime(when);
     tmp_usec=when->tv_usec + (msecs * 1000);
     when->tv_usec=tmp_usec % 1000000;
     when->tv_sec+=tmp_usec / 1000000;
@@ -449,6 +498,7 @@ static void ignore_handler(int signal_num)
 #define FATAL(X) IFTRAP(X) signal(X, fatal_signal_handler);
 #define IGNORE(X) IFTRAP(X) signal(X, SIG_IGN)
 
+
 void mainloop_trap_signals(const sigset_t *which)
 {
     struct sigaction sa;
@@ -460,6 +510,7 @@ void mainloop_trap_signals(const sigset_t *which)
         which=&dummy;
     }
     
+    sigemptyset(&special_sigs);
     sigemptyset(&set);
     sigemptyset(&oldset);
     sigprocmask(SIG_SETMASK, &set, &oldset);
@@ -483,24 +534,28 @@ void mainloop_trap_signals(const sigset_t *which)
         sa.sa_handler=timer_handler;
         sa.sa_flags=SA_RESTART;
         sigaction(SIGALRM, &sa, NULL);
+        sigaddset(&special_sigs, SIGALRM);
     }
 
     IFTRAP(SIGCHLD){
         sa.sa_handler=chld_handler;
         sa.sa_flags=SA_NOCLDSTOP|SA_RESTART;
         sigaction(SIGCHLD, &sa, NULL);
+        sigaddset(&special_sigs, SIGCHLD);
     }
 
     IFTRAP(SIGUSR2){
         sa.sa_handler=usr2_handler;
         sa.sa_flags=SA_RESTART;
         sigaction(SIGUSR2, &sa, NULL);
+        sigaddset(&special_sigs, SIGUSR2);
     }
 
     IFTRAP(SIGTERM){
         sa.sa_handler=exit_handler;
         sa.sa_flags=SA_RESTART;
         sigaction(SIGTERM, &sa, NULL);
+        sigaddset(&special_sigs, SIGTERM);
     }
     
     IFTRAP(SIGUSR1){