emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] trunk r117596: On GNU/Linux, use timerfd for asynchronous


From: Dmitry Antipov
Subject: [Emacs-diffs] trunk r117596: On GNU/Linux, use timerfd for asynchronous timers.
Date: Mon, 28 Jul 2014 06:29:45 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 117596
revision-id: address@hidden
parent: address@hidden
committer: Dmitry Antipov <address@hidden>
branch nick: trunk
timestamp: Mon 2014-07-28 10:28:15 +0400
message:
  On GNU/Linux, use timerfd for asynchronous timers.
  * configure.ac (toplevel): Check whether GNU/Linux-specific
  timerfd functions and macros are available.
  * m4/clock_time.m4 (gl_CLOCK_TIME): Check for clock_getres as well.
  * src/atimer.c (toplevel) [HAVE_TIMERFD]: Include sys/timerfd.h.
  (toplevel): Rename alarm_timer_ok to special_timer_available.
  [HAVE_TIMERFD]: Declare timerfd.
  [HAVE_CLOCK_GETRES]: Declare resolution.
  (start_atimer) [HAVE_CLOCK_GETRES]: Round up timestamp to
  system timer resolution.
  (set_alarm) [HAVE_TIMERFD]: Use timerfd_settime.
  (timerfd_callback) [HAVE_TIMERFD]: New function.
  (atimer_result, debug_timer_callback, Fdebug_timer_check)
  [ENABLE_CHECKING]: New function for the sake of automated tests.
  (init_atimer) [HAVE_TIMERFD]: Setup timerfd.
  [HAVE_CLOCK_GETRES]: Likewise for system timer resolution.
  [ENABLE_CHECKING]: Defsubr test function.
  * src/atimer.h (timerfd_callback) [HAVE_TIMERFD]: Add prototype.
  * src/lisp.h (add_timer_wait_descriptor) [HAVE_TIMERFD]: Likewise.
  * src/process.c (add_timer_wait_descriptor) [HAVE_TIMERFD]: New function.
  * test/automated/timer-tests.el (timer-tests-debug-timer-check): New test.
modified:
  ChangeLog                      changelog-20091113204419-o5vbwnq5f7feedwu-1538
  configure.ac                   
configure.in-20091113204419-o5vbwnq5f7feedwu-783
  m4/clock_time.m4               
clock_time.m4-20120622212453-poe1wduuhk4mz8vy-13
  src/ChangeLog                  changelog-20091113204419-o5vbwnq5f7feedwu-1438
  src/atimer.c                   atimer.c-20091113204419-o5vbwnq5f7feedwu-1759
  src/atimer.h                   atimer.h-20091113204419-o5vbwnq5f7feedwu-1760
  src/lisp.h                     lisp.h-20091113204419-o5vbwnq5f7feedwu-253
  src/process.c                  process.c-20091113204419-o5vbwnq5f7feedwu-462
  test/ChangeLog                 changelog-20091113204419-o5vbwnq5f7feedwu-8588
  test/automated/timer-tests.el  timertests.el-20131018030924-fs4oywoym2ynuoh6-1
=== modified file 'ChangeLog'
--- a/ChangeLog 2014-07-13 15:50:35 +0000
+++ b/ChangeLog 2014-07-28 06:28:15 +0000
@@ -1,3 +1,9 @@
+2014-07-28  Dmitry Antipov  <address@hidden>
+
+       * configure.ac (toplevel): Check whether GNU/Linux-specific
+       timerfd functions and macros are available.
+       * m4/clock_time.m4 (gl_CLOCK_TIME): Check for clock_getres as well.
+
 2014-07-13  Paul Eggert  <address@hidden>
 
        Improve behavior of 'bzr up; cd src; make -k'.

=== modified file 'configure.ac'
--- a/configure.ac      2014-07-10 12:33:35 +0000
+++ b/configure.ac      2014-07-28 06:28:15 +0000
@@ -3710,6 +3710,26 @@
 AC_SUBST(LIBS_TERMCAP)
 AC_SUBST(TERMCAP_OBJ)
 
+# GNU/Linux-specific timer functions.
+if test $opsys = gnu-linux; then
+  AC_MSG_CHECKING([whether Linux timerfd functions are supported])
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/timerfd.h>]],
+                                    [[timerfd_create (CLOCK_REALTIME, 0);
+                                      timerfd_settime (0, 0, NULL, NULL)]])],
+  emacs_cv_linux_timerfd=yes, emacs_cv_linux_timerfd=no)
+  AC_MSG_RESULT([$emacs_cv_linux_timerfd])
+  if test $emacs_cv_linux_timerfd = yes; then
+    AC_DEFINE(HAVE_TIMERFD, 1, [Define to 1 if Linux timerfd functions are 
supported.])
+    AC_MSG_CHECKING([whether TFD_CLOEXEC is defined])
+      AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/timerfd.h>]],
+                                        [[timerfd_create (CLOCK_REALTIME, 
TFD_CLOEXEC)]])],
+    emacs_cv_tfd_cloexec=yes, emacs_cv_tfd_cloexec=no)
+    AC_MSG_RESULT([$emacs_cv_tfd_cloexec])
+    if test $emacs_cv_tfd_cloexec = yes; then
+      AC_DEFINE(HAVE_TIMERFD_CLOEXEC, 1, [Define to 1 if TFD_CLOEXEC is 
defined.])
+    fi
+  fi
+fi  
 
 # Do we have res_init, for detecting changes in /etc/resolv.conf?
 # On Darwin, res_init appears not to be useful: see bug#562 and

=== modified file 'm4/clock_time.m4'
--- a/m4/clock_time.m4  2014-01-01 07:43:34 +0000
+++ b/m4/clock_time.m4  2014-07-28 06:28:15 +0000
@@ -26,6 +26,6 @@
     AC_SEARCH_LIBS([clock_gettime], [rt posix4],
                    [test "$ac_cv_search_clock_gettime" = "none required" ||
                     LIB_CLOCK_GETTIME=$ac_cv_search_clock_gettime])
-    AC_CHECK_FUNCS([clock_gettime clock_settime])
+    AC_CHECK_FUNCS([clock_getres clock_gettime clock_settime])
   LIBS=$gl_saved_libs
 ])

=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2014-07-28 02:13:11 +0000
+++ b/src/ChangeLog     2014-07-28 06:28:15 +0000
@@ -1,3 +1,23 @@
+2014-07-28  Dmitry Antipov  <address@hidden>
+
+       On GNU/Linux, use timerfd for asynchronous timers.
+       * atimer.c (toplevel) [HAVE_TIMERFD]: Include sys/timerfd.h.
+       (toplevel): Rename alarm_timer_ok to special_timer_available.
+       [HAVE_TIMERFD]: Declare timerfd.
+       [HAVE_CLOCK_GETRES]: Declare resolution.
+       (start_atimer) [HAVE_CLOCK_GETRES]: Round up timestamp to
+       system timer resolution.
+       (set_alarm) [HAVE_TIMERFD]: Use timerfd_settime.
+       (timerfd_callback) [HAVE_TIMERFD]: New function.
+       (atimer_result, debug_timer_callback, Fdebug_timer_check)
+       [ENABLE_CHECKING]: New function for the sake of automated tests.
+       (init_atimer) [HAVE_TIMERFD]: Setup timerfd.
+       [HAVE_CLOCK_GETRES]: Likewise for system timer resolution.
+       [ENABLE_CHECKING]: Defsubr test function.
+       * atimer.h (timerfd_callback) [HAVE_TIMERFD]: Add prototype.
+       * lisp.h (add_timer_wait_descriptor) [HAVE_TIMERFD]: Likewise.
+       * process.c (add_timer_wait_descriptor) [HAVE_TIMERFD]: New function.
+
 2014-07-28  Paul Eggert  <address@hidden>
 
        * frame.c (x_set_frame_parameters): Don't use uninitialized locals.

=== modified file 'src/atimer.c'
--- a/src/atimer.c      2014-07-25 17:14:01 +0000
+++ b/src/atimer.c      2014-07-28 06:28:15 +0000
@@ -26,6 +26,15 @@
 #include "atimer.h"
 #include <unistd.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.  */
 
 static struct atimer *free_atimers;
@@ -40,11 +49,23 @@
 
 static struct atimer *atimers;
 
-/* The alarm timer and whether it was properly initialized, if
-   POSIX timers are available.  */
-#ifdef HAVE_ITIMERSPEC
+#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;
-static bool alarm_timer_ok;
+#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
+
+#ifdef HAVE_CLOCK_GETRES
+/* Resolution of CLOCK_REALTIME clock.  */
+static struct timespec resolution;
 #endif
 
 /* Block/unblock SIGALRM.  */
@@ -96,11 +117,16 @@
   struct atimer *t;
   sigset_t oldset;
 
-  /* Round TIME up to the next full second if we don't have
-     itimers.  */
-#ifndef HAVE_SETITIMER
+#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
@@ -285,16 +311,25 @@
 #endif
       struct timespec now, interval;
 
-#ifdef HAVE_ITIMERSPEC
-      if (alarm_timer_ok)
+#if defined (HAVE_TIMERFD) || defined (HAVE_ITIMERSPEC)
+      if (special_timer_available)
        {
          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;
        }
-#endif
+#endif /* HAVE_TIMERFD || HAVE_ITIMERSPEC */
 
       /* Determine interval till the next timer is ripe.
         Don't set the interval to 0; this disables the timer.  */
@@ -373,6 +408,15 @@
   pending_signals = 1;
 }
 
+#ifdef HAVE_TIMERFD
+
+void
+timerfd_callback (int fd, void *arg)
+{
+  do_pending_atimers ();
+}
+
+#endif /* HAVE_TIMERFD */
 
 /* Do pending timers.  */
 
@@ -401,21 +445,106 @@
     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)
 {
-#ifdef HAVE_ITIMERSPEC
+#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;
-  alarm_timer_ok = timer_create (CLOCK_REALTIME, &sigev, &alarm_timer) == 0;
-#endif
+  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_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  
 }

=== modified file 'src/atimer.h'
--- a/src/atimer.h      2014-01-01 07:43:34 +0000
+++ b/src/atimer.h      2014-07-28 06:28:15 +0000
@@ -77,5 +77,8 @@
 void turn_on_atimers (bool);
 void stop_other_atimers (struct atimer *);
 void run_all_atimers (void);
+#ifdef HAVE_TIMERFD
+void timerfd_callback (int, void *);
+#endif
 
 #endif /* EMACS_ATIMER_H */

=== modified file 'src/lisp.h'
--- a/src/lisp.h        2014-07-26 13:17:25 +0000
+++ b/src/lisp.h        2014-07-28 06:28:15 +0000
@@ -4186,6 +4186,9 @@
 #else
 # define WAIT_READING_MAX INTMAX_MAX
 #endif
+#ifdef HAVE_TIMERFD
+extern void add_timer_wait_descriptor (int);
+#endif
 extern void add_keyboard_wait_descriptor (int);
 extern void delete_keyboard_wait_descriptor (int);
 #ifdef HAVE_GPM

=== modified file 'src/process.c'
--- a/src/process.c     2014-07-26 13:17:25 +0000
+++ b/src/process.c     2014-07-28 06:28:15 +0000
@@ -6827,6 +6827,24 @@
 /* The following functions are needed even if async subprocesses are
    not supported.  Some of them are no-op stubs in that case.  */
 
+#ifdef HAVE_TIMERFD
+
+/* Add FD, which is a descriptor returned by timerfd_create,
+   to the set of non-keyboard input descriptors.  */
+
+void
+add_timer_wait_descriptor (int fd)
+{
+  FD_SET (fd, &non_keyboard_wait_mask);
+  fd_callback_info[fd].func = timerfd_callback;
+  fd_callback_info[fd].data = NULL;
+  fd_callback_info[fd].condition |= FOR_READ;
+  if (fd > max_input_desc)
+    max_input_desc = fd;
+}
+
+#endif /* HAVE_TIMERFD */
+
 /* Add DESC to the set of keyboard input descriptors.  */
 
 void

=== modified file 'test/ChangeLog'
--- a/test/ChangeLog    2014-07-26 12:53:36 +0000
+++ b/test/ChangeLog    2014-07-28 06:28:15 +0000
@@ -1,3 +1,7 @@
+2014-07-28  Dmitry Antipov  <address@hidden>
+
+       * automated/timer-tests.el (timer-tests-debug-timer-check): New test.
+
 2014-07-26  Ulf Jasper  <address@hidden>
 
        * automated/icalendar-tests.el (icalendar-tests--do-test-import):

=== modified file 'test/automated/timer-tests.el'
--- a/test/automated/timer-tests.el     2014-01-01 07:43:34 +0000
+++ b/test/automated/timer-tests.el     2014-07-28 06:28:15 +0000
@@ -34,5 +34,9 @@
     (sit-for 0 t)
     (should timer-ran)))
 
+(ert-deftest timer-tests-debug-timer-check ()
+  ;; This function exists only if --enable-checking.
+  (if (fboundp 'debug-timer-check)
+      (should (debug-timer-check)) t))
+
 ;;; timer-tests.el ends here
-


reply via email to

[Prev in Thread] Current Thread [Next in Thread]