From eb7fe81e6db8d630521098a728713e10c9d59c74 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 5 Aug 2022 10:38:59 -0400 Subject: 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. --- lisp/emacs-lisp/timer.el | 68 +++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 29 deletions(-) (limited to 'lisp/emacs-lisp/timer.el') diff --git a/lisp/emacs-lisp/timer.el b/lisp/emacs-lisp/timer.el index fd29abf40a3..aafb2e684f4 100644 --- a/lisp/emacs-lisp/timer.el +++ b/lisp/emacs-lisp/timer.el @@ -159,32 +159,42 @@ SECS may be a fraction." timer) (defun timer--activate (timer &optional triggered-p reuse-cell idle) - (if (and (timerp timer) - (integerp (timer--high-seconds timer)) - (integerp (timer--low-seconds timer)) - (integerp (timer--usecs timer)) - (integerp (timer--psecs timer)) - (timer--function timer)) - (let ((timers (if idle timer-idle-list timer-list)) - last) - ;; Skip all timers to trigger before the new one. - (while (and timers (timer--time-less-p (car timers) timer)) - (setq last timers - timers (cdr timers))) - (if reuse-cell - (progn - (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) - (error "Invalid or uninitialized timer"))) + (let ((timers (if idle timer-idle-list timer-list)) + last) + (cond + ((not (and (timerp timer) + (integerp (timer--high-seconds timer)) + (integerp (timer--low-seconds timer)) + (integerp (timer--usecs timer)) + (integerp (timer--psecs timer)) + (timer--function timer))) + (error "Invalid or uninitialized timer")) + ;; FIXME: This is not reliable because `idle-delay' is only set late, + ;; by `timer-activate-when-idle' :-( + ;;((not (eq (not idle) + ;; (not (timer--idle-delay timer)))) + ;; (error "idle arg %S out of sync with idle-delay field of timer: %S" + ;; idle timer)) + ((memq timer timers) + (error "Timer already activated")) + (t + ;; Skip all timers to trigger before the new one. + (while (and timers (timer--time-less-p (car timers) timer)) + (setq last timers + timers (cdr timers))) + (if reuse-cell + (progn + (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) "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--activate timer (not dont-wait) reuse-cell 'idle)) -(defalias 'disable-timeout 'cancel-timer) +(defalias 'disable-timeout #'cancel-timer) (defun cancel-timer (timer) "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'." (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) "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 (list (read-from-minibuffer "Run after idle (seconds): " nil nil t) (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))) (timer-set-function timer function args) (timer-set-idle-time timer secs repeat) -- cgit v1.2.3 From b70369c557efed3dcd86dc64a2e73e3480dea6af Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 5 Aug 2022 18:46:31 -0400 Subject: time-convert): Deprecate calls without an explicit FORM arg * lisp/subr.el (time-convert): Deprecate calls without an explicit FORM arg. * doc/lispref/os.texi (Time Conversion): Adjust doc accordingly. * lisp/calendar/time-date.el (days-to-time): * lisp/emacs-lisp/timer.el (timer-next-integral-multiple-of-time): * lisp/gnus/nnrss.el (nnrss-normalize-date): * lisp/epa-ks.el (epa-ks--parse-buffer): Silence corresponding warnings. --- doc/lispref/os.texi | 4 ++-- etc/NEWS | 4 ++++ lisp/calendar/time-date.el | 5 ++++- lisp/emacs-lisp/timer.el | 2 +- lisp/epa-ks.el | 6 ++---- lisp/gnus/nnrss.el | 4 ++-- lisp/subr.el | 1 + 7 files changed, 16 insertions(+), 10 deletions(-) (limited to 'lisp/emacs-lisp/timer.el') diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 5fb34fb9b66..d591b219cd0 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -1541,7 +1541,7 @@ Year numbers count since the year 1 BCE, and do not skip zero as traditional Gregorian years do; for example, the year number @minus{}37 represents the Gregorian year 38 BCE@. -@defun time-convert time &optional form +@defun time-convert time form This function converts a time value into a Lisp timestamp. The optional @var{form} argument specifies the timestamp form to be @@ -1554,7 +1554,7 @@ representing the timestamp; for example, it is treated as 1000000000 if @var{time} is @code{nil} and the platform timestamp has nanosecond resolution. If @var{form} is @code{list}, this function returns an integer list @code{(@var{high} @var{low} @var{micro} @var{pico})}. -Although an omitted or @code{nil} @var{form} currently acts like +Although a @code{nil} @var{form} currently acts like @code{list}, this is planned to change in a future Emacs version, so callers requiring list timestamps should pass @code{list} explicitly. diff --git a/etc/NEWS b/etc/NEWS index 7145a8861eb..dc8bd6ce24b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2525,6 +2525,10 @@ patcomp.el, pc-mode.el, pc-select.el, s-region.el, and sregex.el. * Lisp Changes in Emacs 29.1 +** The FORM arg of 'time-convert' is mandatory. +'time-convert' can still be called without it, as before, but the +compiler now emits a warning about this deprecated usage. + +++ ** Emacs now supports user-customizable and themable icons. These can be used for buttons in buffers and the like. See diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el index 7c99d05dc3f..b80b47a33fa 100644 --- a/lisp/calendar/time-date.el +++ b/lisp/calendar/time-date.el @@ -179,7 +179,10 @@ If DATE lacks timezone information, GMT is assumed." ;;;###autoload (defun days-to-time (days) "Convert DAYS into a time value." - (let ((time (time-convert (* 86400 days)))) + ;; FIXME: We should likely just pass `t' to `time-convert'. + ;; All uses I could find in Emacs, GNU ELPA, and NonGNU ELPA can handle + ;; any valid time representation as return value. + (let ((time (time-convert (* 86400 days) 'list))) ;; Traditionally, this returned a two-element list if DAYS was an integer. ;; Keep that tradition if time-convert outputs timestamps in list form. (if (and (integerp days) (consp (cdr time))) diff --git a/lisp/emacs-lisp/timer.el b/lisp/emacs-lisp/timer.el index aafb2e684f4..b25a040a96c 100644 --- a/lisp/emacs-lisp/timer.el +++ b/lisp/emacs-lisp/timer.el @@ -122,7 +122,7 @@ of SECS seconds since the epoch. SECS may be a fraction." (setq ticks (ash ticks 1)) (setq hz (ash hz 1))) (let ((more-ticks (+ ticks trunc-s-ticks))) - (time-convert (cons (- more-ticks (% more-ticks trunc-s-ticks)) hz))))) + (time-convert (cons (- more-ticks (% more-ticks trunc-s-ticks)) hz) t)))) (defun timer-relative-time (time secs &optional usecs psecs) "Advance TIME by SECS seconds. diff --git a/lisp/epa-ks.el b/lisp/epa-ks.el index f41429f7734..7c60b659f0a 100644 --- a/lisp/epa-ks.el +++ b/lisp/epa-ks.el @@ -295,13 +295,11 @@ enough, since keyservers have strict timeout settings." :created (and (match-string 4) (not (string-empty-p (match-string 4))) - (time-convert - (string-to-number (match-string 4)))) + (time-convert (string-to-number (match-string 4)) t)) :expires (and (match-string 5) (not (string-empty-p (match-string 5))) - (time-convert - (string-to-number (match-string 5)))) + (time-convert (string-to-number (match-string 5)) t)) :flags (mapcar (lambda (flag) (cdr (assq flag '((?r revoked) diff --git a/lisp/gnus/nnrss.el b/lisp/gnus/nnrss.el index 5047be1a6a5..8c96d3e0678 100644 --- a/lisp/gnus/nnrss.el +++ b/lisp/gnus/nnrss.el @@ -453,8 +453,8 @@ which RSS 2.0 allows." (let (case-fold-search vector year month day time zone given) (cond ((null date)) ; do nothing for this case ;; if the date is just digits (unix time stamp): - ((string-match "^[0-9]+$" date) - (setq given (time-convert (string-to-number date)))) + ((string-match "\\`[0-9]+\\'" date) + (setq given (time-convert (string-to-number date) t))) ;; RFC 822 ((string-match " [0-9]+ " date) (setq vector (timezone-parse-date date) diff --git a/lisp/subr.el b/lisp/subr.el index 2603b5ad251..4b1fc832da1 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -1857,6 +1857,7 @@ be a list of the form returned by `event-start' and `event-end'." (set-advertised-calling-convention 'redirect-frame-focus '(frame focus-frame) "24.3") (set-advertised-calling-convention 'libxml-parse-xml-region '(start end &optional base-url) "27.1") (set-advertised-calling-convention 'libxml-parse-html-region '(start end &optional base-url) "27.1") +(set-advertised-calling-convention 'time-convert '(time form) "29.1") ;;;; Obsolescence declarations for variables, and aliases. -- cgit v1.2.3