summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Antipov <dmantipov@yandex.ru>2014-07-28 10:28:15 +0400
committerDmitry Antipov <dmantipov@yandex.ru>2014-07-28 10:28:15 +0400
commit768b24eb0e880c0b39e36fd089905cdca572a758 (patch)
tree75899b103fedc549e5d17294d39bfcb4ed0a9bc9
parent7daa4ff121ad8da6e609b959d5c95796e5d3a9eb (diff)
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.
-rw-r--r--ChangeLog6
-rw-r--r--configure.ac20
-rw-r--r--m4/clock_time.m42
-rw-r--r--src/ChangeLog20
-rw-r--r--src/atimer.c155
-rw-r--r--src/atimer.h3
-rw-r--r--src/lisp.h3
-rw-r--r--src/process.c18
-rw-r--r--test/ChangeLog4
-rw-r--r--test/automated/timer-tests.el6
10 files changed, 222 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index f694a42202..c7fd47636a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2014-07-28 Dmitry Antipov <dmantipov@yandex.ru>
+
+ * 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 <eggert@cs.ucla.edu>
Improve behavior of 'bzr up; cd src; make -k'.
diff --git a/configure.ac b/configure.ac
index fc7a87a075..89440446ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3710,6 +3710,26 @@ fi
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
diff --git a/m4/clock_time.m4 b/m4/clock_time.m4
index 6c4a637dc6..8513c6d781 100644
--- a/m4/clock_time.m4
+++ b/m4/clock_time.m4
@@ -26,6 +26,6 @@ AC_DEFUN([gl_CLOCK_TIME],
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
])
diff --git a/src/ChangeLog b/src/ChangeLog
index b3f3750df7..ebc412c886 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,23 @@
+2014-07-28 Dmitry Antipov <dmantipov@yandex.ru>
+
+ 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 <eggert@cs.ucla.edu>
* frame.c (x_set_frame_parameters): Don't use uninitialized locals.
diff --git a/src/atimer.c b/src/atimer.c
index c4f062beb1..9079e7712e 100644
--- a/src/atimer.c
+++ b/src/atimer.c
@@ -26,6 +26,15 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#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 *stopped_atimers;
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 @@ start_atimer (enum atimer_type type, struct timespec timestamp,
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 @@ set_alarm (void)
#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 @@ handle_alarm_signal (int sig)
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 @@ turn_on_atimers (bool on)
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
}
diff --git a/src/atimer.h b/src/atimer.h
index 379787abca..2386977cf8 100644
--- a/src/atimer.h
+++ b/src/atimer.h
@@ -77,5 +77,8 @@ void init_atimer (void);
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 */
diff --git a/src/lisp.h b/src/lisp.h
index bf25f073d4..67299706c6 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4186,6 +4186,9 @@ extern int wait_reading_process_output (intmax_t, int, int, bool, Lisp_Object,
#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
diff --git a/src/process.c b/src/process.c
index 4449493a2b..cfc1e189ca 100644
--- a/src/process.c
+++ b/src/process.c
@@ -6827,6 +6827,24 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
/* 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
diff --git a/test/ChangeLog b/test/ChangeLog
index 6afa9ea895..140b5d04ef 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,7 @@
+2014-07-28 Dmitry Antipov <dmantipov@yandex.ru>
+
+ * automated/timer-tests.el (timer-tests-debug-timer-check): New test.
+
2014-07-26 Ulf Jasper <ulf.jasper@web.de>
* automated/icalendar-tests.el (icalendar-tests--do-test-import):
diff --git a/test/automated/timer-tests.el b/test/automated/timer-tests.el
index bc95df5e77..f8e8414f8d 100644
--- a/test/automated/timer-tests.el
+++ b/test/automated/timer-tests.el
@@ -34,5 +34,9 @@
(sit-for 0 t)
(should timer-ran)))
-;;; timer-tests.el ends here
+(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