[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
-
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Emacs-diffs] trunk r117596: On GNU/Linux, use timerfd for asynchronous timers.,
Dmitry Antipov <=