]> code.delx.au - gnu-emacs/blobdiff - src/atimer.c
* xterm.c (x_sync_with_move): Really wait 0.5s, not 0.0005s.
[gnu-emacs] / src / atimer.c
index e72bc305753c2403eec4b76870ef321909aa9b65..c03ac96c6dac87a826a335540ecb4abcd68f6c66 100644 (file)
@@ -1,5 +1,5 @@
 /* Asynchronous timers.
-   Copyright (C) 2000-201 Free Software Foundation, Inc.
+   Copyright (C) 2000-2014 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -17,16 +17,23 @@ You should have received a copy of the GNU General Public License
 along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
-#include <signal.h>
 #include <stdio.h>
-#include <setjmp.h>
+
 #include "lisp.h"
 #include "syssignal.h"
 #include "systime.h"
 #include "blockinput.h"
 #include "atimer.h"
 #include <unistd.h>
-#include <sys/time.h>
+
+#ifdef HAVE_TIMERFD
+#include <sys/timerfd.h>
+#ifdef HAVE_TIMERFD_CLOEXEC
+#define TIMERFD_CREATE_FLAGS TFD_CLOEXEC
+#else
+#define TIMERFD_CREATE_FLAGS 0
+#endif /* HAVE_TIMERFD_CLOEXEC */
+#endif /* HAVE_TIMERFD */
 
 /* Free-list of atimer structures.  */
 
@@ -42,18 +49,41 @@ static struct atimer *stopped_atimers;
 
 static struct atimer *atimers;
 
-/* Non-zero means alarm_signal_handler has found ripe timers but
-   interrupt_input_blocked was non-zero.  In this case, timer
-   functions are not called until the next UNBLOCK_INPUT because timer
-   functions are expected to call X, and X cannot be assumed to be
-   reentrant.  */
+#if defined (HAVE_TIMERFD)
+/* File descriptor returned by timerfd_create.  GNU/Linux-specific.  */
+static int timerfd;
+#elif defined (HAVE_ITIMERSPEC)
+/* The alarm timer used if POSIX timers are available.  */
+static timer_t alarm_timer;
+#endif
+
+#if defined (HAVE_TIMERFD) || defined (HAVE_ITIMERSPEC)
+/* Non-zero if one of the above was successfully initialized.  Do not
+   use bool due to special treatment if HAVE_TIMERFD, see below.  */
+static int special_timer_available;
+#endif
 
-int pending_atimers;
+#ifdef HAVE_CLOCK_GETRES
+/* Resolution of CLOCK_REALTIME clock.  */
+static struct timespec resolution;
+#endif
 
 /* Block/unblock SIGALRM.  */
 
-#define BLOCK_ATIMERS   sigblock (sigmask (SIGALRM))
-#define UNBLOCK_ATIMERS sigunblock (sigmask (SIGALRM))
+static void
+block_atimers (sigset_t *oldset)
+{
+  sigset_t blocked;
+  sigemptyset (&blocked);
+  sigaddset (&blocked, SIGALRM);
+  sigaddset (&blocked, SIGINT);
+  pthread_sigmask (SIG_BLOCK, &blocked, oldset);
+}
+static void
+unblock_atimers (sigset_t const *oldset)
+{
+  pthread_sigmask (SIG_SETMASK, oldset, 0);
+}
 
 /* Function prototypes.  */
 
@@ -61,8 +91,6 @@ static void set_alarm (void);
 static void schedule_atimer (struct atimer *);
 static struct atimer *append_atimer_lists (struct atimer *,
                                            struct atimer *);
-static void alarm_signal_handler (int signo);
-
 
 /* Start a new atimer of type TYPE.  TIME specifies when the timer is
    ripe.  FN is the function to call when the timer fires.
@@ -83,20 +111,22 @@ static void alarm_signal_handler (int signo);
    to cancel_atimer; don't free it yourself.  */
 
 struct atimer *
-start_atimer (enum atimer_type type, EMACS_TIME timestamp, atimer_callback fn,
-             void *client_data)
+start_atimer (enum atimer_type type, struct timespec timestamp,
+             atimer_callback fn, void *client_data)
 {
   struct atimer *t;
-
-  /* Round TIME up to the next full second if we don't have
-     itimers.  */
-#ifndef HAVE_SETITIMER
-  if (EMACS_NSECS (timestamp) != 0
-      && EMACS_SECS (timestamp) < TYPE_MAXIMUM (time_t))
-    {
-      EMACS_SET_USECS (timestamp, 0);
-      EMACS_SET_SECS (timestamp, EMACS_SECS (timestamp) + 1);
-    }
+  sigset_t oldset;
+
+#if !defined (HAVE_SETITIMER)
+  /* Round TIME up to the next full second if we don't have itimers.  */
+  if (timestamp.tv_nsec != 0 && timestamp.tv_sec < TYPE_MAXIMUM (time_t))
+    timestamp = make_timespec (timestamp.tv_sec + 1, 0);
+#elif defined (HAVE_CLOCK_GETRES)
+  /* Check that the system clock is precise enough.  If
+     not, round TIME up to the system clock resolution.  */
+  if (timespec_valid_p (resolution)
+      && timespec_cmp (timestamp, resolution) < 0)
+    timestamp = resolution;
 #endif /* not HAVE_SETITIMER */
 
   /* Get an atimer structure from the free-list, or allocate
@@ -107,7 +137,7 @@ start_atimer (enum atimer_type type, EMACS_TIME timestamp, atimer_callback fn,
       free_atimers = t->next;
     }
   else
-    t = (struct atimer *) xmalloc (sizeof *t);
+    t = xmalloc (sizeof *t);
 
   /* Fill the atimer structure.  */
   memset (t, 0, sizeof *t);
@@ -115,7 +145,7 @@ start_atimer (enum atimer_type type, EMACS_TIME timestamp, atimer_callback fn,
   t->fn = fn;
   t->client_data = client_data;
 
-  BLOCK_ATIMERS;
+  block_atimers (&oldset);
 
   /* Compute the timer's expiration time.  */
   switch (type)
@@ -125,20 +155,18 @@ start_atimer (enum atimer_type type, EMACS_TIME timestamp, atimer_callback fn,
       break;
 
     case ATIMER_RELATIVE:
-      EMACS_GET_TIME (t->expiration);
-      EMACS_ADD_TIME (t->expiration, t->expiration, timestamp);
+      t->expiration = timespec_add (current_timespec (), timestamp);
       break;
 
     case ATIMER_CONTINUOUS:
-      EMACS_GET_TIME (t->expiration);
-      EMACS_ADD_TIME (t->expiration, t->expiration, timestamp);
+      t->expiration = timespec_add (current_timespec (), timestamp);
       t->interval = timestamp;
       break;
     }
 
   /* Insert the timer in the list of active atimers.  */
   schedule_atimer (t);
-  UNBLOCK_ATIMERS;
+  unblock_atimers (&oldset);
 
   /* Arrange for a SIGALRM at the time the next atimer is ripe.  */
   set_alarm ();
@@ -153,8 +181,9 @@ void
 cancel_atimer (struct atimer *timer)
 {
   int i;
+  sigset_t oldset;
 
-  BLOCK_ATIMERS;
+  block_atimers (&oldset);
 
   for (i = 0; i < 2; ++i)
     {
@@ -181,7 +210,7 @@ cancel_atimer (struct atimer *timer)
        }
     }
 
-  UNBLOCK_ATIMERS;
+  unblock_atimers (&oldset);
 }
 
 
@@ -212,7 +241,8 @@ append_atimer_lists (struct atimer *list_1, struct atimer *list_2)
 void
 stop_other_atimers (struct atimer *t)
 {
-  BLOCK_ATIMERS;
+  sigset_t oldset;
+  block_atimers (&oldset);
 
   if (t)
     {
@@ -237,22 +267,23 @@ stop_other_atimers (struct atimer *t)
 
   stopped_atimers = append_atimer_lists (atimers, stopped_atimers);
   atimers = t;
-  UNBLOCK_ATIMERS;
+  unblock_atimers (&oldset);
 }
 
 
 /* Run all timers again, if some have been stopped with a call to
    stop_other_atimers.  */
 
-static void
+void
 run_all_atimers (void)
 {
   if (stopped_atimers)
     {
       struct atimer *t = atimers;
       struct atimer *next;
+      sigset_t oldset;
 
-      BLOCK_ATIMERS;
+      block_atimers (&oldset);
       atimers = stopped_atimers;
       stopped_atimers = NULL;
 
@@ -263,21 +294,11 @@ run_all_atimers (void)
          t = next;
        }
 
-      UNBLOCK_ATIMERS;
+      unblock_atimers (&oldset);
     }
 }
 
 
-/* A version of run_all_atimers suitable for a record_unwind_protect.  */
-
-Lisp_Object
-unwind_stop_other_atimers (Lisp_Object dummy)
-{
-  run_all_atimers ();
-  return Qnil;
-}
-
-
 /* Arrange for a SIGALRM to arrive when the next timer is ripe.  */
 
 static void
@@ -285,31 +306,45 @@ set_alarm (void)
 {
   if (atimers)
     {
-      EMACS_TIME now, timestamp;
 #ifdef HAVE_SETITIMER
       struct itimerval it;
 #endif
+      struct timespec now, interval;
 
-      /* Determine s/us till the next timer is ripe.  */
-      EMACS_GET_TIME (now);
-
-      /* Don't set the interval to 0; this disables the timer.  */
-      if (EMACS_TIME_LE (atimers->expiration, now))
+#if defined (HAVE_TIMERFD) || defined (HAVE_ITIMERSPEC)
+      if (special_timer_available)
        {
-         EMACS_SET_SECS (timestamp, 0);
-         EMACS_SET_USECS (timestamp, 1000);
+         struct itimerspec ispec;
+         ispec.it_value = atimers->expiration;
+         ispec.it_interval.tv_sec = ispec.it_interval.tv_nsec = 0;
+#if defined (HAVE_TIMERFD)
+         if (special_timer_available == 1)
+           {
+             add_timer_wait_descriptor (timerfd);
+             special_timer_available++;
+           }
+         if (timerfd_settime (timerfd, TFD_TIMER_ABSTIME, &ispec, 0) == 0)
+#elif defined (HAVE_ITIMERSPEC)
+         if (timer_settime (alarm_timer, TIMER_ABSTIME, &ispec, 0) == 0)
+#endif     
+           return;
        }
-      else
-       EMACS_SUB_TIME (timestamp, atimers->expiration, now);
+#endif /* HAVE_TIMERFD || HAVE_ITIMERSPEC */
 
+      /* Determine interval till the next timer is ripe.
+        Don't set the interval to 0; this disables the timer.  */
+      now = current_timespec ();
+      interval = (timespec_cmp (atimers->expiration, now) <= 0
+                 ? make_timespec (0, 1000 * 1000)
+                 : timespec_sub (atimers->expiration, now));
 
 #ifdef HAVE_SETITIMER
 
       memset (&it, 0, sizeof it);
-      it.it_value = make_timeval (timestamp);
+      it.it_value = make_timeval (interval);
       setitimer (ITIMER_REAL, &it, 0);
 #else /* not HAVE_SETITIMER */
-      alarm (max (EMACS_SECS (timestamp), 1));
+      alarm (max (interval.tv_sec, 1));
 #endif /* not HAVE_SETITIMER */
     }
 }
@@ -325,7 +360,7 @@ schedule_atimer (struct atimer *t)
   struct atimer *a = atimers, *prev = NULL;
 
   /* Look for the first atimer that is ripe after T.  */
-  while (a && EMACS_TIME_GT (t->expiration, a->expiration))
+  while (a && timespec_cmp (a->expiration, t->expiration) < 0)
     prev = a, a = a->next;
 
   /* Insert T in front of the atimer found, if any.  */
@@ -340,22 +375,17 @@ schedule_atimer (struct atimer *t)
 static void
 run_timers (void)
 {
-  EMACS_TIME now;
+  struct timespec now = current_timespec ();
 
-  while (atimers
-        && (pending_atimers = interrupt_input_blocked) == 0
-        && (EMACS_GET_TIME (now),
-            EMACS_TIME_LE (atimers->expiration, now)))
+  while (atimers && timespec_cmp (atimers->expiration, now) <= 0)
     {
-      struct atimer *t;
-
-      t = atimers;
+      struct atimer *t = atimers;
       atimers = atimers->next;
       t->fn (t);
 
       if (t->type == ATIMER_CONTINUOUS)
        {
-         EMACS_ADD_TIME (t->expiration, now, t->interval);
+         t->expiration = timespec_add (now, t->interval);
          schedule_atimer (t);
        }
       else
@@ -365,53 +395,50 @@ run_timers (void)
        }
     }
 
-  if (! atimers)
-    pending_atimers = 0;
-
-#ifdef SYNC_INPUT
-  if (pending_atimers)
-    pending_signals = 1;
-  else
-    {
-      pending_signals = interrupt_input_pending;
-      set_alarm ();
-    }
-#else
-  if (! pending_atimers)
-    set_alarm ();
-#endif
+  set_alarm ();
 }
 
 
 /* Signal handler for SIGALRM.  SIGNO is the signal number, i.e.
    SIGALRM.  */
 
-void
-alarm_signal_handler (int signo)
+static void
+handle_alarm_signal (int sig)
 {
-#ifndef SYNC_INPUT
-  SIGNAL_THREAD_CHECK (signo);
-#endif
-
-  pending_atimers = 1;
-#ifdef SYNC_INPUT
   pending_signals = 1;
-#else
-  run_timers ();
-#endif
 }
 
+#ifdef HAVE_TIMERFD
+
+/* Called from wait_reading_process_output when FD, which
+   should be equal to TIMERFD, is available for reading.  */
+
+void
+timerfd_callback (int fd, void *arg)
+{
+  char buf[8];
+  ptrdiff_t nbytes;
+
+  eassert (fd == timerfd);
+  nbytes = emacs_read (fd, buf, sizeof (buf));
+  /* Just discard an expiration count for now.  */
+  eassert (nbytes == sizeof (buf));
+  do_pending_atimers ();
+}
+
+#endif /* HAVE_TIMERFD */
 
-/* Call alarm_signal_handler for pending timers.  */
+/* Do pending timers.  */
 
 void
 do_pending_atimers (void)
 {
-  if (pending_atimers)
+  if (atimers)
     {
-      BLOCK_ATIMERS;
+      sigset_t oldset;
+      block_atimers (&oldset);
       run_timers ();
-      UNBLOCK_ATIMERS;
+      unblock_atimers (&oldset);
     }
 }
 
@@ -420,23 +447,125 @@ do_pending_atimers (void)
    some systems like HPUX (see process.c).  */
 
 void
-turn_on_atimers (int on)
+turn_on_atimers (bool on)
 {
   if (on)
+    set_alarm ();
+  else
     {
-      signal (SIGALRM, alarm_signal_handler);
-      set_alarm ();
+#ifdef HAVE_TIMERFD
+      if (special_timer_available > 1)
+       {
+         struct itimerspec ispec;
+         memset (&ispec, 0, sizeof (ispec));
+         /* Writing zero expiration time should disarm it.  */
+         timerfd_settime (timerfd, TFD_TIMER_ABSTIME, &ispec, 0);
+       }
+#endif /* HAVE_TIMERFD */
+      alarm (0);
     }
-  else
-    alarm (0);
 }
 
+/* This is intended to use from automated tests.  */
+
+#ifdef ENABLE_CHECKING
+
+#define MAXTIMERS 10
+
+struct atimer_result
+{
+  /* Time when we expect this timer to trigger.  */
+  struct timespec expected;
+
+  /* Timer status: -1 if not triggered, 0 if triggered
+     too early or too late, 1 if triggered timely.  */
+  int intime;
+};
+
+static void
+debug_timer_callback (struct atimer *t)
+{
+  struct timespec now = current_timespec ();
+  struct atimer_result *r = (struct atimer_result *) t->client_data;
+  int result = timespec_cmp (now, r->expected);
+
+  if (result < 0)
+    /* Too early.  */
+    r->intime = 0;
+  else if (result >= 0)
+    {
+#ifdef HAVE_SETITIMER      
+      struct timespec delta = timespec_sub (now, r->expected);
+      /* Too late if later than expected + 0.01s.  FIXME:
+        this should depend from system clock resolution.  */
+      if (timespec_cmp (delta, make_timespec (0, 10000000)) > 0)
+       r->intime = 0;
+      else
+#endif /* HAVE_SETITIMER */    
+       r->intime = 1;
+    }
+}
+
+DEFUN ("debug-timer-check", Fdebug_timer_check, Sdebug_timer_check, 0, 0, 0,
+       doc: /* Run internal self-tests to check timers subsystem.
+Return t if all self-tests are passed, nil otherwise.  */)
+  (void)
+{
+  int i, ok;
+  struct atimer *timer;
+  struct atimer_result *results[MAXTIMERS];
+  struct timespec t = make_timespec (0, 0);
+
+  /* Arm MAXTIMERS relative timers to trigger with 0.1s intervals.  */
+  for (i = 0; i < MAXTIMERS; i++)
+    {
+      results[i] = xmalloc (sizeof (struct atimer_result));
+      t = timespec_add (t, make_timespec (0, 100000000));
+      results[i]->expected = timespec_add (current_timespec (), t);
+      results[i]->intime = -1;
+      timer = start_atimer (ATIMER_RELATIVE, t,
+                           debug_timer_callback, results[i]);
+    }
+
+  /* Wait for 1s but process timers.  */
+  wait_reading_process_output (1, 0, 0, false, Qnil, NULL, 0);
+  /* Shut up the compiler by "using" this variable.  */
+  (void) timer;
+
+  for (i = 0, ok = 0; i < MAXTIMERS; i++)
+    ok += results[i]->intime, xfree (results[i]);
+
+  return ok == MAXTIMERS ? Qt : Qnil;
+}
+
+#endif /* ENABLE_CHECKING */
 
 void
 init_atimer (void)
 {
+#if defined (HAVE_TIMERFD)
+  timerfd = timerfd_create (CLOCK_REALTIME, TIMERFD_CREATE_FLAGS);
+  special_timer_available = !!(timerfd != -1);
+#elif defined (HAVE_ITIMERSPEC)
+  struct sigevent sigev;
+  sigev.sigev_notify = SIGEV_SIGNAL;
+  sigev.sigev_signo = SIGALRM;
+  sigev.sigev_value.sival_ptr = &alarm_timer;
+  special_timer_available
+    = timer_create (CLOCK_REALTIME, &sigev, &alarm_timer) == 0;
+#endif /* HAVE_TIMERFD */
+#ifdef HAVE_CLOCK_GETRES
+  if (clock_getres (CLOCK_REALTIME, &resolution))
+    resolution = invalid_timespec ();
+#endif  
   free_atimers = stopped_atimers = atimers = NULL;
-  pending_atimers = 0;
-  /* pending_signals is initialized in init_keyboard.*/
-  signal (SIGALRM, alarm_signal_handler);
+
+  /* pending_signals is initialized in init_keyboard.  */
+  struct sigaction action;
+  emacs_sigaction_init (&action, handle_alarm_signal);
+  sigaction (SIGALRM, &action, 0);
+
+#ifdef ENABLE_CHECKING
+  defsubr (&Sdebug_timer_check);
+#endif  
 }