X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/2ab329f3b5d52a39f0a45c3d9c129f1c19560142..aac6be13316f360bb2df46d6708ad9eea6059dc5:/src/atimer.c diff --git a/src/atimer.c b/src/atimer.c index 5dbd807872..ce782f6adb 100644 --- a/src/atimer.c +++ b/src/atimer.c @@ -1,5 +1,5 @@ /* Asynchronous timers. - Copyright (C) 2000-2012 Free Software Foundation, Inc. + Copyright (C) 2000-2014 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -26,6 +26,11 @@ along with GNU Emacs. If not, see . */ #include "atimer.h" #include +#ifdef HAVE_TIMERFD +#include +# include +#endif + /* Free-list of atimer structures. */ static struct atimer *free_atimers; @@ -40,33 +45,35 @@ static struct atimer *stopped_atimers; static struct atimer *atimers; -/* Non-zero means alarm signal handler has found ripe timers but - interrupt_input_blocked was non-zero. In this case, timer - functions are not called until the next UNBLOCK_INPUT because timer - functions are expected to call X, and X cannot be assumed to be - reentrant. */ - -int pending_atimers; +#ifdef HAVE_ITIMERSPEC +/* The alarm timer and whether it was properly initialized, if + POSIX timers are available. */ +static timer_t alarm_timer; +static bool alarm_timer_ok; + +# ifdef HAVE_TIMERFD +/* File descriptor for timer, or -1 if it could not be created. */ +static int timerfd; +# else +enum { timerfd = -1 }; +# endif +#endif /* Block/unblock SIGALRM. */ static void -sigmask_atimers (int how) +block_atimers (sigset_t *oldset) { sigset_t blocked; sigemptyset (&blocked); sigaddset (&blocked, SIGALRM); - pthread_sigmask (how, &blocked, 0); + sigaddset (&blocked, SIGINT); + pthread_sigmask (SIG_BLOCK, &blocked, oldset); } static void -block_atimers (void) +unblock_atimers (sigset_t const *oldset) { - sigmask_atimers (SIG_BLOCK); -} -static void -unblock_atimers (void) -{ - sigmask_atimers (SIG_UNBLOCK); + pthread_sigmask (SIG_SETMASK, oldset, 0); } /* Function prototypes. */ @@ -76,36 +83,35 @@ static void schedule_atimer (struct atimer *); static struct atimer *append_atimer_lists (struct atimer *, struct atimer *); -/* Start a new atimer of type TYPE. TIME specifies when the timer is +/* Start a new atimer of type TYPE. TIMESTAMP specifies when the timer is ripe. FN is the function to call when the timer fires. CLIENT_DATA is stored in the client_data member of the atimer structure returned and so made available to FN when it is called. - If TYPE is ATIMER_ABSOLUTE, TIME is the absolute time at which the + If TYPE is ATIMER_ABSOLUTE, TIMESTAMP is the absolute time at which the timer fires. - If TYPE is ATIMER_RELATIVE, the timer is ripe TIME s/us in the + If TYPE is ATIMER_RELATIVE, the timer is ripe TIMESTAMP seconds in the future. In both cases, the timer is automatically freed after it has fired. - If TYPE is ATIMER_CONTINUOUS, the timer fires every TIME s/us. + If TYPE is ATIMER_CONTINUOUS, the timer fires every TIMESTAMP seconds. Value is a pointer to the atimer started. It can be used in calls to cancel_atimer; don't free it yourself. */ struct atimer * -start_atimer (enum atimer_type type, EMACS_TIME timestamp, atimer_callback fn, - void *client_data) +start_atimer (enum atimer_type type, struct timespec timestamp, + atimer_callback fn, void *client_data) { struct atimer *t; + sigset_t oldset; - /* Round TIME up to the next full second if we don't have - itimers. */ + /* Round TIMESTAMP up to the next full second if we don't have itimers. */ #ifndef HAVE_SETITIMER - if (EMACS_NSECS (timestamp) != 0 - && EMACS_SECS (timestamp) < TYPE_MAXIMUM (time_t)) - timestamp = make_emacs_time (EMACS_SECS (timestamp) + 1, 0); + if (timestamp.tv_nsec != 0 && timestamp.tv_sec < TYPE_MAXIMUM (time_t)) + timestamp = make_timespec (timestamp.tv_sec + 1, 0); #endif /* not HAVE_SETITIMER */ /* Get an atimer structure from the free-list, or allocate @@ -124,7 +130,7 @@ start_atimer (enum atimer_type type, EMACS_TIME timestamp, atimer_callback fn, t->fn = fn; t->client_data = client_data; - block_atimers (); + block_atimers (&oldset); /* Compute the timer's expiration time. */ switch (type) @@ -134,18 +140,18 @@ start_atimer (enum atimer_type type, EMACS_TIME timestamp, atimer_callback fn, break; case ATIMER_RELATIVE: - t->expiration = add_emacs_time (current_emacs_time (), timestamp); + t->expiration = timespec_add (current_timespec (), timestamp); break; case ATIMER_CONTINUOUS: - t->expiration = add_emacs_time (current_emacs_time (), timestamp); + t->expiration = timespec_add (current_timespec (), timestamp); t->interval = timestamp; break; } /* Insert the timer in the list of active atimers. */ schedule_atimer (t); - unblock_atimers (); + unblock_atimers (&oldset); /* Arrange for a SIGALRM at the time the next atimer is ripe. */ set_alarm (); @@ -160,8 +166,9 @@ void cancel_atimer (struct atimer *timer) { int i; + sigset_t oldset; - block_atimers (); + block_atimers (&oldset); for (i = 0; i < 2; ++i) { @@ -188,7 +195,7 @@ cancel_atimer (struct atimer *timer) } } - unblock_atimers (); + unblock_atimers (&oldset); } @@ -219,7 +226,8 @@ append_atimer_lists (struct atimer *list_1, struct atimer *list_2) void stop_other_atimers (struct atimer *t) { - block_atimers (); + sigset_t oldset; + block_atimers (&oldset); if (t) { @@ -244,22 +252,23 @@ stop_other_atimers (struct atimer *t) stopped_atimers = append_atimer_lists (atimers, stopped_atimers); atimers = t; - unblock_atimers (); + unblock_atimers (&oldset); } /* Run all timers again, if some have been stopped with a call to stop_other_atimers. */ -static void +void run_all_atimers (void) { if (stopped_atimers) { struct atimer *t = atimers; struct atimer *next; + sigset_t oldset; - block_atimers (); + block_atimers (&oldset); atimers = stopped_atimers; stopped_atimers = NULL; @@ -270,21 +279,11 @@ run_all_atimers (void) t = next; } - unblock_atimers (); + unblock_atimers (&oldset); } } -/* A version of run_all_atimers suitable for a record_unwind_protect. */ - -Lisp_Object -unwind_stop_other_atimers (Lisp_Object dummy) -{ - run_all_atimers (); - return Qnil; -} - - /* Arrange for a SIGALRM to arrive when the next timer is ripe. */ static void @@ -295,14 +294,33 @@ set_alarm (void) #ifdef HAVE_SETITIMER struct itimerval it; #endif + struct timespec now, interval; - /* Determine s/us till the next timer is ripe. */ - EMACS_TIME now = current_emacs_time (); +#ifdef HAVE_ITIMERSPEC + if (0 <= timerfd || alarm_timer_ok) + { + struct itimerspec ispec; + ispec.it_value = atimers->expiration; + ispec.it_interval.tv_sec = ispec.it_interval.tv_nsec = 0; +# ifdef HAVE_TIMERFD + if (timerfd_settime (timerfd, TFD_TIMER_ABSTIME, &ispec, 0) == 0) + { + add_timer_wait_descriptor (timerfd); + return; + } +# endif + if (alarm_timer_ok + && timer_settime (alarm_timer, TIMER_ABSTIME, &ispec, 0) == 0) + return; + } +#endif - /* Don't set the interval to 0; this disables the timer. */ - EMACS_TIME interval = (EMACS_TIME_LE (atimers->expiration, now) - ? make_emacs_time (0, 1000 * 1000) - : sub_emacs_time (atimers->expiration, now)); + /* Determine interval till the next timer is ripe. + Don't set the interval to 0; this disables the timer. */ + now = current_timespec (); + interval = (timespec_cmp (atimers->expiration, now) <= 0 + ? make_timespec (0, 1000 * 1000) + : timespec_sub (atimers->expiration, now)); #ifdef HAVE_SETITIMER @@ -310,7 +328,7 @@ set_alarm (void) it.it_value = make_timeval (interval); setitimer (ITIMER_REAL, &it, 0); #else /* not HAVE_SETITIMER */ - alarm (max (EMACS_SECS (interval), 1)); + alarm (max (interval.tv_sec, 1)); #endif /* not HAVE_SETITIMER */ } } @@ -326,7 +344,7 @@ schedule_atimer (struct atimer *t) struct atimer *a = atimers, *prev = NULL; /* Look for the first atimer that is ripe after T. */ - while (a && EMACS_TIME_GT (t->expiration, a->expiration)) + while (a && timespec_cmp (a->expiration, t->expiration) < 0) prev = a, a = a->next; /* Insert T in front of the atimer found, if any. */ @@ -341,22 +359,17 @@ schedule_atimer (struct atimer *t) static void run_timers (void) { - EMACS_TIME now; + struct timespec now = current_timespec (); - while (atimers - && (pending_atimers = interrupt_input_blocked) == 0 - && (now = current_emacs_time (), - EMACS_TIME_LE (atimers->expiration, now))) + while (atimers && timespec_cmp (atimers->expiration, now) <= 0) { - struct atimer *t; - - t = atimers; + struct atimer *t = atimers; atimers = atimers->next; t->fn (t); if (t->type == ATIMER_CONTINUOUS) { - t->expiration = add_emacs_time (now, t->interval); + t->expiration = timespec_add (now, t->interval); schedule_atimer (t); } else @@ -366,16 +379,7 @@ run_timers (void) } } - if (! atimers) - pending_atimers = 0; - - if (pending_atimers) - pending_signals = 1; - else - { - pending_signals = interrupt_input_pending; - set_alarm (); - } + set_alarm (); } @@ -385,27 +389,52 @@ run_timers (void) static void handle_alarm_signal (int sig) { - pending_atimers = 1; pending_signals = 1; } -static void -deliver_alarm_signal (int sig) +#ifdef HAVE_TIMERFD + +/* Called from wait_reading_process_output when FD, which + should be equal to TIMERFD, is available for reading. */ + +void +timerfd_callback (int fd, void *arg) { - handle_on_main_thread (sig, handle_alarm_signal); + ptrdiff_t nbytes; + uint64_t expirations; + + eassert (fd == timerfd); + nbytes = emacs_read (fd, &expirations, sizeof (expirations)); + + if (nbytes == sizeof (expirations)) + { + /* Timer should expire just once. */ + eassert (expirations == 1); + do_pending_atimers (); + } + else if (nbytes < 0) + /* For some not yet known reason, we may get weird event and no + data on timer descriptor. This can break Gnus at least, see: + http://lists.gnu.org/archive/html/emacs-devel/2014-07/msg00503.html. */ + eassert (errno == EAGAIN); + else + /* I don't know what else can happen with this descriptor. */ + emacs_abort (); } +#endif /* HAVE_TIMERFD */ -/* Call alarm signal handler for pending timers. */ +/* Do pending timers. */ void do_pending_atimers (void) { - if (pending_atimers) + if (atimers) { - block_atimers (); + sigset_t oldset; + block_atimers (&oldset); run_timers (); - unblock_atimers (); + unblock_atimers (&oldset); } } @@ -417,24 +446,123 @@ void turn_on_atimers (bool on) { if (on) + set_alarm (); + else { - struct sigaction action; - emacs_sigaction_init (&action, deliver_alarm_signal); - sigaction (SIGALRM, &action, 0); - set_alarm (); +#ifdef HAVE_ITIMERSPEC + struct itimerspec ispec; + memset (&ispec, 0, sizeof ispec); + if (alarm_timer_ok) + timer_settime (alarm_timer, TIMER_ABSTIME, &ispec, 0); +# ifdef HAVE_TIMERFD + timerfd_settime (timerfd, TFD_TIMER_ABSTIME, &ispec, 0); +# endif +#endif + 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; } - else - alarm (0); } +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) { - struct sigaction action; +#ifdef HAVE_ITIMERSPEC +# ifdef HAVE_TIMERFD + /* Until this feature is considered stable, you can ask to not use it. */ + timerfd = (egetenv ("EMACS_IGNORE_TIMERFD") ? -1 : + timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC)); +# endif + if (timerfd < 0) + { + 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 free_atimers = stopped_atimers = atimers = NULL; - pending_atimers = 0; - /* pending_signals is initialized in init_keyboard.*/ - emacs_sigaction_init (&action, deliver_alarm_signal); + + /* 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 }