aboutsummaryrefslogtreecommitdiffstats
path: root/src/atimer.c
diff options
context:
space:
mode:
authorDmitry Antipov2014-07-28 10:28:15 +0400
committerDmitry Antipov2014-07-28 10:28:15 +0400
commit768b24eb0e880c0b39e36fd089905cdca572a758 (patch)
tree75899b103fedc549e5d17294d39bfcb4ed0a9bc9 /src/atimer.c
parent7daa4ff121ad8da6e609b959d5c95796e5d3a9eb (diff)
downloademacs-768b24eb0e880c0b39e36fd089905cdca572a758.tar.gz
emacs-768b24eb0e880c0b39e36fd089905cdca572a758.zip
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.
Diffstat (limited to 'src/atimer.c')
-rw-r--r--src/atimer.c155
1 files changed, 142 insertions, 13 deletions
diff --git a/src/atimer.c b/src/atimer.c
index c4f062beb17..9079e7712e0 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/>. */
26#include "atimer.h" 26#include "atimer.h"
27#include <unistd.h> 27#include <unistd.h>
28 28
29#ifdef HAVE_TIMERFD
30#include <sys/timerfd.h>
31#ifdef HAVE_TIMERFD_CLOEXEC
32#define TIMERFD_CREATE_FLAGS TFD_CLOEXEC
33#else
34#define TIMERFD_CREATE_FLAGS 0
35#endif /* HAVE_TIMERFD_CLOEXEC */
36#endif /* HAVE_TIMERFD */
37
29/* Free-list of atimer structures. */ 38/* Free-list of atimer structures. */
30 39
31static struct atimer *free_atimers; 40static struct atimer *free_atimers;
@@ -40,11 +49,23 @@ static struct atimer *stopped_atimers;
40 49
41static struct atimer *atimers; 50static struct atimer *atimers;
42 51
43/* The alarm timer and whether it was properly initialized, if 52#if defined (HAVE_TIMERFD)
44 POSIX timers are available. */ 53/* File descriptor returned by timerfd_create. GNU/Linux-specific. */
45#ifdef HAVE_ITIMERSPEC 54static int timerfd;
55#elif defined (HAVE_ITIMERSPEC)
56/* The alarm timer used if POSIX timers are available. */
46static timer_t alarm_timer; 57static timer_t alarm_timer;
47static bool alarm_timer_ok; 58#endif
59
60#if defined (HAVE_TIMERFD) || defined (HAVE_ITIMERSPEC)
61/* Non-zero if one of the above was successfully initialized. Do not
62 use bool due to special treatment if HAVE_TIMERFD, see below. */
63static int special_timer_available;
64#endif
65
66#ifdef HAVE_CLOCK_GETRES
67/* Resolution of CLOCK_REALTIME clock. */
68static struct timespec resolution;
48#endif 69#endif
49 70
50/* Block/unblock SIGALRM. */ 71/* Block/unblock SIGALRM. */
@@ -96,11 +117,16 @@ start_atimer (enum atimer_type type, struct timespec timestamp,
96 struct atimer *t; 117 struct atimer *t;
97 sigset_t oldset; 118 sigset_t oldset;
98 119
99 /* Round TIME up to the next full second if we don't have 120#if !defined (HAVE_SETITIMER)
100 itimers. */ 121 /* Round TIME up to the next full second if we don't have itimers. */
101#ifndef HAVE_SETITIMER
102 if (timestamp.tv_nsec != 0 && timestamp.tv_sec < TYPE_MAXIMUM (time_t)) 122 if (timestamp.tv_nsec != 0 && timestamp.tv_sec < TYPE_MAXIMUM (time_t))
103 timestamp = make_timespec (timestamp.tv_sec + 1, 0); 123 timestamp = make_timespec (timestamp.tv_sec + 1, 0);
124#elif defined (HAVE_CLOCK_GETRES)
125 /* Check that the system clock is precise enough. If
126 not, round TIME up to the system clock resolution. */
127 if (timespec_valid_p (resolution)
128 && timespec_cmp (timestamp, resolution) < 0)
129 timestamp = resolution;
104#endif /* not HAVE_SETITIMER */ 130#endif /* not HAVE_SETITIMER */
105 131
106 /* Get an atimer structure from the free-list, or allocate 132 /* Get an atimer structure from the free-list, or allocate
@@ -285,16 +311,25 @@ set_alarm (void)
285#endif 311#endif
286 struct timespec now, interval; 312 struct timespec now, interval;
287 313
288#ifdef HAVE_ITIMERSPEC 314#if defined (HAVE_TIMERFD) || defined (HAVE_ITIMERSPEC)
289 if (alarm_timer_ok) 315 if (special_timer_available)
290 { 316 {
291 struct itimerspec ispec; 317 struct itimerspec ispec;
292 ispec.it_value = atimers->expiration; 318 ispec.it_value = atimers->expiration;
293 ispec.it_interval.tv_sec = ispec.it_interval.tv_nsec = 0; 319 ispec.it_interval.tv_sec = ispec.it_interval.tv_nsec = 0;
320#if defined (HAVE_TIMERFD)
321 if (special_timer_available == 1)
322 {
323 add_timer_wait_descriptor (timerfd);
324 special_timer_available++;
325 }
326 if (timerfd_settime (timerfd, TFD_TIMER_ABSTIME, &ispec, 0) == 0)
327#elif defined (HAVE_ITIMERSPEC)
294 if (timer_settime (alarm_timer, TIMER_ABSTIME, &ispec, 0) == 0) 328 if (timer_settime (alarm_timer, TIMER_ABSTIME, &ispec, 0) == 0)
329#endif
295 return; 330 return;
296 } 331 }
297#endif 332#endif /* HAVE_TIMERFD || HAVE_ITIMERSPEC */
298 333
299 /* Determine interval till the next timer is ripe. 334 /* Determine interval till the next timer is ripe.
300 Don't set the interval to 0; this disables the timer. */ 335 Don't set the interval to 0; this disables the timer. */
@@ -373,6 +408,15 @@ handle_alarm_signal (int sig)
373 pending_signals = 1; 408 pending_signals = 1;
374} 409}
375 410
411#ifdef HAVE_TIMERFD
412
413void
414timerfd_callback (int fd, void *arg)
415{
416 do_pending_atimers ();
417}
418
419#endif /* HAVE_TIMERFD */
376 420
377/* Do pending timers. */ 421/* Do pending timers. */
378 422
@@ -401,21 +445,106 @@ turn_on_atimers (bool on)
401 alarm (0); 445 alarm (0);
402} 446}
403 447
448/* This is intended to use from automated tests. */
449
450#ifdef ENABLE_CHECKING
451
452#define MAXTIMERS 10
453
454struct atimer_result
455{
456 /* Time when we expect this timer to trigger. */
457 struct timespec expected;
458
459 /* Timer status: -1 if not triggered, 0 if triggered
460 too early or too late, 1 if triggered timely. */
461 int intime;
462};
463
464static void
465debug_timer_callback (struct atimer *t)
466{
467 struct timespec now = current_timespec ();
468 struct atimer_result *r = (struct atimer_result *) t->client_data;
469 int result = timespec_cmp (now, r->expected);
470
471 if (result < 0)
472 /* Too early. */
473 r->intime = 0;
474 else if (result >= 0)
475 {
476#ifdef HAVE_SETITIMER
477 struct timespec delta = timespec_sub (now, r->expected);
478 /* Too late if later than expected + 0.01s. FIXME:
479 this should depend from system clock resolution. */
480 if (timespec_cmp (delta, make_timespec (0, 10000000)) > 0)
481 r->intime = 0;
482 else
483#endif /* HAVE_SETITIMER */
484 r->intime = 1;
485 }
486}
487
488DEFUN ("debug-timer-check", Fdebug_timer_check, Sdebug_timer_check, 0, 0, 0,
489 doc: /* Run internal self-tests to check timers subsystem.
490Return t if all self-tests are passed, nil otherwise. */)
491 (void)
492{
493 int i, ok;
494 struct atimer *timer;
495 struct atimer_result *results[MAXTIMERS];
496 struct timespec t = make_timespec (0, 0);
497
498 /* Arm MAXTIMERS relative timers to trigger with 0.1s intervals. */
499 for (i = 0; i < MAXTIMERS; i++)
500 {
501 results[i] = xmalloc (sizeof (struct atimer_result));
502 t = timespec_add (t, make_timespec (0, 100000000));
503 results[i]->expected = timespec_add (current_timespec (), t);
504 results[i]->intime = -1;
505 timer = start_atimer (ATIMER_RELATIVE, t,
506 debug_timer_callback, results[i]);
507 }
508
509 /* Wait for 1s but process timers. */
510 wait_reading_process_output (1, 0, 0, false, Qnil, NULL, 0);
511 /* Shut up the compiler by "using" this variable. */
512 (void) timer;
513
514 for (i = 0, ok = 0; i < MAXTIMERS; i++)
515 ok += results[i]->intime, xfree (results[i]);
516
517 return ok == MAXTIMERS ? Qt : Qnil;
518}
519
520#endif /* ENABLE_CHECKING */
404 521
405void 522void
406init_atimer (void) 523init_atimer (void)
407{ 524{
408#ifdef HAVE_ITIMERSPEC 525#if defined (HAVE_TIMERFD)
526 timerfd = timerfd_create (CLOCK_REALTIME, TIMERFD_CREATE_FLAGS);
527 special_timer_available = !!(timerfd != -1);
528#elif defined (HAVE_ITIMERSPEC)
409 struct sigevent sigev; 529 struct sigevent sigev;
410 sigev.sigev_notify = SIGEV_SIGNAL; 530 sigev.sigev_notify = SIGEV_SIGNAL;
411 sigev.sigev_signo = SIGALRM; 531 sigev.sigev_signo = SIGALRM;
412 sigev.sigev_value.sival_ptr = &alarm_timer; 532 sigev.sigev_value.sival_ptr = &alarm_timer;
413 alarm_timer_ok = timer_create (CLOCK_REALTIME, &sigev, &alarm_timer) == 0; 533 special_timer_available
414#endif 534 = timer_create (CLOCK_REALTIME, &sigev, &alarm_timer) == 0;
535#endif /* HAVE_TIMERFD */
536#ifdef HAVE_CLOCK_GETRES
537 if (clock_getres (CLOCK_REALTIME, &resolution))
538 resolution = invalid_timespec ();
539#endif
415 free_atimers = stopped_atimers = atimers = NULL; 540 free_atimers = stopped_atimers = atimers = NULL;
416 541
417 /* pending_signals is initialized in init_keyboard. */ 542 /* pending_signals is initialized in init_keyboard. */
418 struct sigaction action; 543 struct sigaction action;
419 emacs_sigaction_init (&action, handle_alarm_signal); 544 emacs_sigaction_init (&action, handle_alarm_signal);
420 sigaction (SIGALRM, &action, 0); 545 sigaction (SIGALRM, &action, 0);
546
547#ifdef ENABLE_CHECKING
548 defsubr (&Sdebug_timer_check);
549#endif
421} 550}