]> git.eshelyaron.com Git - emacs.git/commitdiff
Make run-at-time try harder to run at integral multiples
authorLars Ingebrigtsen <larsi@gnus.org>
Tue, 31 Aug 2021 01:04:22 +0000 (03:04 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Tue, 31 Aug 2021 01:04:22 +0000 (03:04 +0200)
* lisp/emacs-lisp/timer.el (timer): Add new slot integral-multiple.
(timerp): Adjust.
(timer-event-handler): Recompute the delay if requested
(bug#39099).
(run-at-time): Mark the timer as recomputable if given a t
parameter.

* src/keyboard.c (decode_timer): Adjust.

etc/NEWS
lisp/emacs-lisp/timer.el
src/keyboard.c

index 657e12900fafb232febfc603cec31e01c730c52a..66006db8e0acc8a6bff05768cfbd518048333cf9 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3145,6 +3145,17 @@ work for `context-menu-mode` in Xterm.
 \f
 * Incompatible Lisp Changes in Emacs 28.1
 
+---
+** 'run-at-time' now tries harder to implement the t TIME parameter.
+If TIME is t, the timer runs at an integral multiple of REPEAT.
+(I.e., if given a REPEAT of 60, it'll run at 08:11:00, 08:12:00,
+08:13:00.)  However, when a machine goes to sleep (or otherwise didn't
+get a time slot to run when the timer was scheduled), the timer would
+then fire every 60 seconds after the time the timer was fired.  This
+has now changed, and the timer code now recomputes the integral
+multiple every time it runs, which means that if the laptop wakes at
+08:16:43, it'll fire at that time, but then at 08:17:00, 08:18:00...
+
 ---
 ** 'parse-partial-sexp' now signals an error if TO is smaller than FROM.
 Previously this would lead to the function interpreting FROM as TO and
index 36de29a73a8fa93b04fc3d7198feabb8e648cee2..f7715109c827d6570c5da216afbeb66892f2afb7 100644 (file)
@@ -29,6 +29,8 @@
 
 (eval-when-compile (require 'cl-lib))
 
+;; If you change this structure, you also have to change `timerp'
+;; (below) and decode_timer in keyboard.c.
 (cl-defstruct (timer
                (:constructor nil)
                (:copier nil)
   repeat-delay
   function args                         ;What to do when triggered.
   idle-delay                            ;If non-nil, this is an idle-timer.
-  psecs)
+  psecs
+  ;; A timer may be created with `t' as the TIME, which means that we
+  ;; want to run at specific integral multiples of `repeat-delay'.  We
+  ;; then have to recompute this (because the machine may have gone to
+  ;; sleep, etc).
+  integral-multiple)
 
 (defun timerp (object)
   "Return t if OBJECT is a timer."
-  (and (vectorp object) (= (length object) 9)))
+  (and (vectorp object) (= (length object) 10)))
 
 (defsubst timer--check (timer)
   (or (timerp timer) (signal 'wrong-type-argument (list #'timerp timer))))
@@ -284,6 +291,13 @@ This function is called, by name, directly by the C code."
                     (if (> repeats timer-max-repeats)
                         (timer-inc-time timer (* (timer--repeat-delay timer)
                                                  repeats)))))
+              ;; If we want integral multiples, we have to recompute
+              ;; the repetition.
+              (when (and (timer--integral-multiple timer)
+                         (not (timer--idle-delay timer)))
+                (setf (timer--time timer)
+                      (timer-next-integral-multiple-of-time
+                       (current-time) (timer--repeat-delay timer))))
               ;; Place it back on the timer-list before running
               ;; timer--function, so it can cancel-timer itself.
               (timer-activate timer t cell)
@@ -340,45 +354,44 @@ This function returns a timer object which you can use in
 `cancel-timer'."
   (interactive "sRun at time: \nNRepeat interval: \naFunction: ")
 
-  (or (null repeat)
-      (and (numberp repeat) (< 0 repeat))
-      (error "Invalid repetition interval"))
+  (when (and repeat
+             (numberp repeat)
+             (< repeat 0))
+    (error "Invalid repetition interval"))
 
-  ;; Special case: nil means "now" and is useful when repeating.
-  (if (null time)
+  (let ((timer (timer-create)))
+    ;; Special case: nil means "now" and is useful when repeating.
+    (unless time
       (setq time (current-time)))
 
-  ;; Special case: t means the next integral multiple of REPEAT.
-  (if (and (eq time t) repeat)
-      (setq time (timer-next-integral-multiple-of-time (current-time) repeat)))
+    ;; Special case: t means the next integral multiple of REPEAT.
+    (when (and (eq time t) repeat)
+      (setq time (timer-next-integral-multiple-of-time (current-time) repeat))
+      (setf (timer--integral-multiple timer) t))
 
-  ;; Handle numbers as relative times in seconds.
-  (if (numberp time)
+    ;; Handle numbers as relative times in seconds.
+    (when (numberp time)
       (setq time (timer-relative-time nil time)))
 
-  ;; Handle relative times like "2 hours 35 minutes"
-  (if (stringp time)
-      (let ((secs (timer-duration time)))
-       (if secs
-           (setq time (timer-relative-time nil secs)))))
-
-  ;; Handle "11:23pm" and the like.  Interpret it as meaning today
-  ;; which admittedly is rather stupid if we have passed that time
-  ;; already.  (Though only Emacs hackers hack Emacs at that time.)
-  (if (stringp time)
-      (progn
-       (require 'diary-lib)
-       (let ((hhmm (diary-entry-time time))
-             (now (decode-time)))
-         (if (>= hhmm 0)
-             (setq time
-                   (encode-time 0 (% hhmm 100) (/ hhmm 100)
-                                 (decoded-time-day now)
-                                (decoded-time-month now)
-                                 (decoded-time-year now)
-                                 (decoded-time-zone now)))))))
+    ;; Handle relative times like "2 hours 35 minutes".
+    (when (stringp time)
+      (when-let ((secs (timer-duration time)))
+       (setq time (timer-relative-time nil secs))))
+
+    ;; Handle "11:23pm" and the like.  Interpret it as meaning today
+    ;; which admittedly is rather stupid if we have passed that time
+    ;; already.  (Though only Emacs hackers hack Emacs at that time.)
+    (when (stringp time)
+      (require 'diary-lib)
+      (let ((hhmm (diary-entry-time time))
+           (now (decode-time)))
+       (when (>= hhmm 0)
+         (setq time (encode-time 0 (% hhmm 100) (/ hhmm 100)
+                                  (decoded-time-day now)
+                                 (decoded-time-month now)
+                                  (decoded-time-year now)
+                                  (decoded-time-zone now))))))
 
-  (let ((timer (timer-create)))
     (timer-set-time timer time repeat)
     (timer-set-function timer function args)
     (timer-activate timer)
index 2e4c4e6aabf2c01175438003bc9fb59b29e32408..81ff9df153d0e6e2d69486c2b3cc39f220e7db9a 100644 (file)
@@ -4234,7 +4234,7 @@ decode_timer (Lisp_Object timer, struct timespec *result)
 {
   Lisp_Object *vec;
 
-  if (! (VECTORP (timer) && ASIZE (timer) == 9))
+  if (! (VECTORP (timer) && ASIZE (timer) == 10))
     return false;
   vec = XVECTOR (timer)->contents;
   if (! NILP (vec[0]))