diff options
| author | Dmitry Antipov | 2014-07-28 10:28:15 +0400 |
|---|---|---|
| committer | Dmitry Antipov | 2014-07-28 10:28:15 +0400 |
| commit | 768b24eb0e880c0b39e36fd089905cdca572a758 (patch) | |
| tree | 75899b103fedc549e5d17294d39bfcb4ed0a9bc9 /src/atimer.c | |
| parent | 7daa4ff121ad8da6e609b959d5c95796e5d3a9eb (diff) | |
| download | emacs-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.c | 155 |
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 | ||
| 31 | static struct atimer *free_atimers; | 40 | static struct atimer *free_atimers; |
| @@ -40,11 +49,23 @@ static struct atimer *stopped_atimers; | |||
| 40 | 49 | ||
| 41 | static struct atimer *atimers; | 50 | static 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 | 54 | static int timerfd; |
| 55 | #elif defined (HAVE_ITIMERSPEC) | ||
| 56 | /* The alarm timer used if POSIX timers are available. */ | ||
| 46 | static timer_t alarm_timer; | 57 | static timer_t alarm_timer; |
| 47 | static 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. */ | ||
| 63 | static int special_timer_available; | ||
| 64 | #endif | ||
| 65 | |||
| 66 | #ifdef HAVE_CLOCK_GETRES | ||
| 67 | /* Resolution of CLOCK_REALTIME clock. */ | ||
| 68 | static 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 | |||
| 413 | void | ||
| 414 | timerfd_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 | |||
| 454 | struct 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 | |||
| 464 | static void | ||
| 465 | debug_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 | |||
| 488 | DEFUN ("debug-timer-check", Fdebug_timer_check, Sdebug_timer_check, 0, 0, 0, | ||
| 489 | doc: /* Run internal self-tests to check timers subsystem. | ||
| 490 | Return 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 | ||
| 405 | void | 522 | void |
| 406 | init_atimer (void) | 523 | init_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 | } |