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.
This commit is contained in:
parent
7daa4ff121
commit
768b24eb0e
10 changed files with 222 additions and 15 deletions
|
@ -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'.
|
||||
|
|
20
configure.ac
20
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
|
||||
|
|
|
@ -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
|
||||
])
|
||||
|
|
|
@ -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.
|
||||
|
|
155
src/atimer.c
155
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
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue