1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-04 11:00:45 -08:00

timer.el: Avoid repeated timers

https://mail.gnu.org/archive/html/emacs-devel/2022-07/msg01127.html
points out that end-users can get bitten by this, accidentally
calling `timer-activate` on an already activated timer.

* lisp/emacs-lisp/timer.el (timer--activate): Signal an error if we try
to re-add a timer that's already on the timer-list.
This commit is contained in:
Stefan Monnier 2022-08-05 10:38:59 -04:00
parent df263dd758
commit eb7fe81e6d

View file

@ -159,32 +159,42 @@ SECS may be a fraction."
timer) timer)
(defun timer--activate (timer &optional triggered-p reuse-cell idle) (defun timer--activate (timer &optional triggered-p reuse-cell idle)
(if (and (timerp timer) (let ((timers (if idle timer-idle-list timer-list))
(integerp (timer--high-seconds timer)) last)
(integerp (timer--low-seconds timer)) (cond
(integerp (timer--usecs timer)) ((not (and (timerp timer)
(integerp (timer--psecs timer)) (integerp (timer--high-seconds timer))
(timer--function timer)) (integerp (timer--low-seconds timer))
(let ((timers (if idle timer-idle-list timer-list)) (integerp (timer--usecs timer))
last) (integerp (timer--psecs timer))
;; Skip all timers to trigger before the new one. (timer--function timer)))
(while (and timers (timer--time-less-p (car timers) timer)) (error "Invalid or uninitialized timer"))
(setq last timers ;; FIXME: This is not reliable because `idle-delay' is only set late,
timers (cdr timers))) ;; by `timer-activate-when-idle' :-(
(if reuse-cell ;;((not (eq (not idle)
(progn ;; (not (timer--idle-delay timer))))
(setcar reuse-cell timer) ;; (error "idle arg %S out of sync with idle-delay field of timer: %S"
(setcdr reuse-cell timers)) ;; idle timer))
(setq reuse-cell (cons timer timers))) ((memq timer timers)
;; Insert new timer after last which possibly means in front of queue. (error "Timer already activated"))
(setf (cond (last (cdr last)) (t
(idle timer-idle-list) ;; Skip all timers to trigger before the new one.
(t timer-list)) (while (and timers (timer--time-less-p (car timers) timer))
reuse-cell) (setq last timers
(setf (timer--triggered timer) triggered-p) timers (cdr timers)))
(setf (timer--idle-delay timer) idle) (if reuse-cell
nil) (progn
(error "Invalid or uninitialized timer"))) (setcar reuse-cell timer)
(setcdr reuse-cell timers))
(setq reuse-cell (cons timer timers)))
;; Insert new timer after last which possibly means in front of queue.
(setf (cond (last (cdr last))
(idle timer-idle-list)
(t timer-list))
reuse-cell)
(setf (timer--triggered timer) triggered-p)
(setf (timer--idle-delay timer) idle)
nil))))
(defun timer-activate (timer &optional triggered-p reuse-cell) (defun timer-activate (timer &optional triggered-p reuse-cell)
"Insert TIMER into `timer-list'. "Insert TIMER into `timer-list'.
@ -216,7 +226,7 @@ the time of the current timer. That's because the activated
timer will fire right away." timer will fire right away."
(timer--activate timer (not dont-wait) reuse-cell 'idle)) (timer--activate timer (not dont-wait) reuse-cell 'idle))
(defalias 'disable-timeout 'cancel-timer) (defalias 'disable-timeout #'cancel-timer)
(defun cancel-timer (timer) (defun cancel-timer (timer)
"Remove TIMER from the list of active timers." "Remove TIMER from the list of active timers."
@ -430,7 +440,7 @@ The action is to call FUNCTION with arguments ARGS.
This function returns a timer object which you can use in `cancel-timer'." This function returns a timer object which you can use in `cancel-timer'."
(interactive "sRun after delay (seconds): \nNRepeat interval: \naFunction: ") (interactive "sRun after delay (seconds): \nNRepeat interval: \naFunction: ")
(apply 'run-at-time secs repeat function args)) (apply #'run-at-time secs repeat function args))
(defun add-timeout (secs function object &optional repeat) (defun add-timeout (secs function object &optional repeat)
"Add a timer to run SECS seconds from now, to call FUNCTION on OBJECT. "Add a timer to run SECS seconds from now, to call FUNCTION on OBJECT.
@ -457,7 +467,7 @@ This function returns a timer object which you can use in `cancel-timer'."
(interactive (interactive
(list (read-from-minibuffer "Run after idle (seconds): " nil nil t) (list (read-from-minibuffer "Run after idle (seconds): " nil nil t)
(y-or-n-p "Repeat each time Emacs is idle? ") (y-or-n-p "Repeat each time Emacs is idle? ")
(intern (completing-read "Function: " obarray 'fboundp t)))) (intern (completing-read "Function: " obarray #'fboundp t))))
(let ((timer (timer-create))) (let ((timer (timer-create)))
(timer-set-function timer function args) (timer-set-function timer function args)
(timer-set-idle-time timer secs repeat) (timer-set-idle-time timer secs repeat)