/* Asynchronous timers. Copyright (C) 2000-2024 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include #include "igc.h" #ifdef WINDOWSNT #define raise(s) w32_raise(s) #endif #include "lisp.h" #include "keyboard.h" #include "syssignal.h" #include "systime.h" #include "atimer.h" #include #ifdef HAVE_TIMERFD #include #include # ifdef CYGWIN # include # endif #endif #ifdef MSDOS #include "msdos.h" #endif /* Free-list of atimer structures. */ static struct atimer *free_atimers; /* List of currently not running timers due to a call to lock_atimer. */ static struct atimer *stopped_atimers; /* List of active atimers, sorted by expiration time. The timer that will become ripe next is always at the front of this list. */ static struct atimer *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 block_atimers (sigset_t *oldset) { sigset_t blocked; sigemptyset (&blocked); sigaddset (&blocked, SIGALRM); sigaddset (&blocked, SIGINT); pthread_sigmask (SIG_BLOCK, &blocked, oldset); } static void unblock_atimers (sigset_t const *oldset) { pthread_sigmask (SIG_SETMASK, oldset, 0); } /* Function prototypes. */ static void set_alarm (void); static void schedule_atimer (struct atimer *); static struct atimer *append_atimer_lists (struct atimer *, struct atimer *); /* 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, TIMESTAMP is the absolute time at which the timer fires. 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 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, struct timespec timestamp, atimer_callback fn, void *client_data) { struct atimer *t; sigset_t oldset; /* Round TIMESTAMP up to the next full second if we don't have itimers. */ #if ! (defined HAVE_ITIMERSPEC || defined HAVE_SETITIMER) if (timestamp.tv_nsec != 0 && timestamp.tv_sec < TYPE_MAXIMUM (time_t)) timestamp = make_timespec (timestamp.tv_sec + 1, 0); #endif /* Get an atimer structure from the free-list, or allocate a new one. */ if (free_atimers) { t = free_atimers; free_atimers = t->next; } else { #ifdef HAVE_MPS t = igc_xzalloc_ambig (sizeof *t); #else t = xmalloc (sizeof *t); #endif } /* Fill the atimer structure. */ memset (t, 0, sizeof *t); t->type = type; t->fn = fn; t->client_data = client_data; block_atimers (&oldset); /* Compute the timer's expiration time. */ switch (type) { case ATIMER_ABSOLUTE: t->expiration = timestamp; break; case ATIMER_RELATIVE: t->expiration = timespec_add (current_timespec (), timestamp); break; case ATIMER_CONTINUOUS: 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 (&oldset); /* Arrange for a SIGALRM at the time the next atimer is ripe. */ set_alarm (); return t; } /* Cancel and free atimer TIMER. */ void cancel_atimer (struct atimer *timer) { int i; sigset_t oldset; block_atimers (&oldset); for (i = 0; i < 2; ++i) { struct atimer *t, *prev; struct atimer **list = i ? &stopped_atimers : &atimers; /* See if TIMER is active or stopped. */ for (t = *list, prev = NULL; t && t != timer; prev = t, t = t->next) ; /* If it is, take it off its list, and put in on the free-list. We don't bother to arrange for setting a different alarm time, since a too early one doesn't hurt. */ if (t) { if (prev) prev->next = t->next; else *list = t->next; t->next = free_atimers; free_atimers = t; break; } } unblock_atimers (&oldset); } /* Append two lists of atimers LIST_1 and LIST_2 and return the result list. */ static struct atimer * append_atimer_lists (struct atimer *list_1, struct atimer *list_2) { if (list_1 == NULL) return list_2; else if (list_2 == NULL) return list_1; else { struct atimer *p; for (p = list_1; p->next; p = p->next) ; p->next = list_2; return list_1; } } /* Stop all timers except timer T. T null means stop all timers. */ void stop_other_atimers (struct atimer *t) { sigset_t oldset; block_atimers (&oldset); if (t) { struct atimer *p, *prev; /* See if T is active. */ for (p = atimers, prev = NULL; p && p != t; prev = p, p = p->next) ; if (p == t) { if (prev) prev->next = t->next; else atimers = t->next; t->next = NULL; } else /* T is not active. Let's handle this like T == 0. */ t = NULL; } stopped_atimers = append_atimer_lists (atimers, stopped_atimers); atimers = t; unblock_atimers (&oldset); } /* Run all timers again, if some have been stopped with a call to stop_other_atimers. */ void run_all_atimers (void) { if (stopped_atimers) { struct atimer *t = atimers; struct atimer *next; sigset_t oldset; block_atimers (&oldset); atimers = stopped_atimers; stopped_atimers = NULL; while (t) { next = t->next; schedule_atimer (t); t = next; } unblock_atimers (&oldset); } } /* Arrange for a SIGALRM to arrive when the next timer is ripe. */ static void set_alarm (void) { if (atimers) { #ifdef HAVE_ITIMERSPEC if (0 <= timerfd || alarm_timer_ok) { bool exit = false; struct itimerspec ispec; ispec.it_value = atimers->expiration; ispec.it_interval.tv_sec = ispec.it_interval.tv_nsec = 0; if (alarm_timer_ok && timer_settime (alarm_timer, TIMER_ABSTIME, &ispec, 0) == 0) exit = true; /* Don't start both timerfd and POSIX timers on Cygwin; this causes a slowdown (bug#51734). Prefer POSIX timers because the timerfd notifications aren't delivered while Emacs is busy, which prevents things like the hourglass pointer from being displayed reliably (bug#19776). */ # ifdef CYGWIN if (exit) return; # endif # ifdef HAVE_TIMERFD if (0 <= timerfd && timerfd_settime (timerfd, TFD_TIMER_ABSTIME, &ispec, 0) == 0) { add_timer_wait_descriptor (timerfd); exit = true; } # endif if (exit) return; } #endif /* Determine interval till the next timer is ripe. */ struct timespec now = current_timespec (); if (timespec_cmp (atimers->expiration, now) <= 0) { /* Timer is (over)due -- just trigger the signal right way. */ raise (SIGALRM); } else { struct timespec interval = timespec_sub (atimers->expiration, now); #ifdef HAVE_SETITIMER struct itimerval it = {.it_value = make_timeval (interval)}; setitimer (ITIMER_REAL, &it, 0); #else alarm (max (interval.tv_sec, 1)); #endif } } } /* Insert timer T into the list of active atimers `atimers', keeping the list sorted by expiration time. T must not be in this list already. */ static void schedule_atimer (struct atimer *t) { struct atimer *a = atimers, *prev = NULL; /* Look for the first atimer that is ripe after T. */ while (a && timespec_cmp (a->expiration, t->expiration) < 0) prev = a, a = a->next; /* Insert T in front of the atimer found, if any. */ if (prev) prev->next = t; else atimers = t; t->next = a; } static void run_timers (void) { struct timespec now = current_timespec (); while (atimers && timespec_cmp (atimers->expiration, now) <= 0) { struct atimer *t = atimers; atimers = atimers->next; t->fn (t); if (t->type == ATIMER_CONTINUOUS) { t->expiration = timespec_add (now, t->interval); schedule_atimer (t); } else { t->next = free_atimers; free_atimers = t; } } set_alarm (); } /* Signal handler for SIGALRM. SIGNO is the signal number, i.e. SIGALRM. */ static void handle_alarm_signal (int sig) { pending_signals = 1; } #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) { 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: https://lists.gnu.org/r/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 */ /* Do pending timers. */ void do_pending_atimers (void) { if (atimers) { sigset_t oldset; block_atimers (&oldset); run_timers (); unblock_atimers (&oldset); } } /* Turn alarms on/off. This seems to be temporarily necessary on some systems like HPUX (see process.c). */ void turn_on_atimers (bool on) { if (on) set_alarm (); else { #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) { bool intime = true; #if defined HAVE_ITIMERSPEC || defined HAVE_SETITIMER struct timespec delta = timespec_sub (now, r->expected); /* Too late if later than expected + 0.02s. FIXME: this should depend from system clock resolution. */ intime = timespec_cmp (delta, make_timespec (0, 20000000)) <= 0; #endif r->intime = intime; } } 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]); } #ifdef HAVE_TIMERFD /* Wait for 1s but process timers. */ wait_reading_process_output (1, 0, 0, false, Qnil, NULL, 0); #else /* If timerfd is not supported, wait_reading_process_output won't pay attention to timers that expired, and the callbacks won't be called. So we need to run the expired timers' callbacks by hand. */ /* Wait 1.2 sec for the timers to expire. */ struct timespec tend = timespec_add (current_timespec (), make_timespec (1, 200000000)); while (timespec_cmp (current_timespec (), tend) < 0) { /* Wait for 5 msec between iterations. */ wait_reading_process_output (0, 5000000, 0, false, Qnil, NULL, 0); if (pending_signals) do_pending_atimers (); } #endif /* 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 */ /* Cygwin has the timerfd interface starting with release 3.0.0, but it is buggy until release 3.0.2. */ #ifdef HAVE_TIMERFD static bool have_buggy_timerfd (void) { # ifdef CYGWIN struct utsname name; return uname (&name) < 0 || strverscmp (name.release, "3.0.2") < 0; # else return false; # endif } #endif void init_atimer (void) { #ifdef HAVE_ITIMERSPEC # ifdef HAVE_TIMERFD /* Until this feature is considered stable, you can ask to not use it. */ timerfd = (egetenv ("EMACS_IGNORE_TIMERFD") || have_buggy_timerfd () ? -1 : timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC)); # endif /* We're starting the alarms even if we have timerfd, because timerfd events do not fire while Emacs Lisp is busy and doesn't call thread_select. This might or might not mean that the timerfd code doesn't really give us anything and should be removed, see discussion in bug#19776. */ 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_signals is initialized in init_keyboard. */ struct sigaction action; emacs_sigaction_init (&action, handle_alarm_signal); sigaction (SIGALRM, &action, 0); #ifdef ENABLE_CHECKING if (!initialized) defsubr (&Sdebug_timer_check); #endif }