diff options
| author | Richard M. Stallman | 1996-01-29 02:19:30 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1996-01-29 02:19:30 +0000 |
| commit | 4395bfdb6aa56010c9922ae71ec18ff485c647bf (patch) | |
| tree | 894fdbde23fe5cc2a7f48399d60c0d70c97e4a7a /lisp/timer.el | |
| parent | 5a8a160eb6c1f4355b6290b3585ed83fad2b7aa2 (diff) | |
| download | emacs-4395bfdb6aa56010c9922ae71ec18ff485c647bf.tar.gz emacs-4395bfdb6aa56010c9922ae71ec18ff485c647bf.zip | |
Sun Jan 28 20:55:10 1996 Richard M. Stallman <rms@mole.gnu.ai.mit.edu>
* timer.el (timer-inc-time): New function.
(run-at-time): Use that.
(run-after-delay): New function.
* timer.el: Add a usecs slot to each timer.
Almost all functions changed.
Sun Jan 28 16:47:55 1996 Morten Welinder <terra@diku.dk>
* timer.el: Complete rewrite to use built-in timer feature.
Diffstat (limited to 'lisp/timer.el')
| -rw-r--r-- | lisp/timer.el | 330 |
1 files changed, 184 insertions, 146 deletions
diff --git a/lisp/timer.el b/lisp/timer.el index b924c2177a0..406c8ba8f41 100644 --- a/lisp/timer.el +++ b/lisp/timer.el | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | ;;; timer.el --- run a function with args at some time in future | 1 | ;;; timers.el --- run a function with args at some time in future |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 1990, 1993, 1994 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1996 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Maintainer: FSF | 5 | ;; Maintainer: FSF |
| 6 | 6 | ||
| @@ -29,155 +29,193 @@ | |||
| 29 | 29 | ||
| 30 | ;;; Code: | 30 | ;;; Code: |
| 31 | 31 | ||
| 32 | (defvar timer-program (expand-file-name "timer" exec-directory) | 32 | ;; Layout of a timer vector: |
| 33 | "The name of the program to run as the timer subprocess. | 33 | ;; [triggered-p trigger-high trigger-low delta-secs function args] |
| 34 | It should normally be in the exec-directory.") | 34 | |
| 35 | 35 | (defun timer-create () | |
| 36 | (defvar timer-process nil) | 36 | "Create a timer object." |
| 37 | (defvar timer-alist ()) | 37 | (let ((timer (make-vector 7 nil))) |
| 38 | (defvar timer-out "") | 38 | (aset timer 0 (make-vector 1 'timer-event)) |
| 39 | (defvar timer-dont-exit nil | 39 | timer)) |
| 40 | ;; this is useful for functions which will be doing their own erratic | 40 | |
| 41 | ;; rescheduling or people who otherwise expect to use the process frequently | 41 | (defun timerp (object) |
| 42 | "If non-nil, don't exit the timer process when no more events are pending.") | 42 | "Return t if OBJECT is a timer." |
| 43 | 43 | (and (vectorp object) (= (length object) 7))) | |
| 44 | ;; Error symbols for timers | 44 | |
| 45 | (put 'timer-error 'error-conditions '(error timer-error)) | 45 | (defun timer-set-time (timer time &optional delta) |
| 46 | (put 'timer-error 'error-message "Timer error") | 46 | "Set the trigger time of TIMER to TIME. |
| 47 | 47 | TIME must be in the internal format returned by, e.g., `current-time' | |
| 48 | (put 'timer-abnormal-termination | 48 | If optional third argument DELTA is a non-zero integer make the timer |
| 49 | 'error-conditions | 49 | fire repeatedly that menu seconds apart." |
| 50 | '(error timer-error timer-abnormal-termination)) | 50 | (or (timerp timer) |
| 51 | (put 'timer-abnormal-termination | 51 | (error "Invalid timer")) |
| 52 | 'error-message | 52 | (aset timer 1 (car time)) |
| 53 | "Timer exited abnormally--all events cancelled") | 53 | (aset timer 2 (if (consp (cdr time)) (car (cdr time)) (cdr time))) |
| 54 | 54 | (aset timer 3 (if (consp (cdr time)) (nth 2 time) 0)) | |
| 55 | (put 'timer-filter-error | 55 | (aset timer 4 (and (integerp delta) (> delta 0) delta)) |
| 56 | 'error-conditions | 56 | timer) |
| 57 | '(error timer-error timer-filter-error)) | 57 | |
| 58 | (put 'timer-filter-error | 58 | |
| 59 | 'error-message | 59 | (defun timer-inc-time (timer secs &optional usecs) |
| 60 | "Error in timer process filter") | 60 | "Increment the time set in TIMER by SECS seconds and USECS microseconds. |
| 61 | 61 | SECS may be a fraction." | |
| 62 | 62 | (or usecs (setq usecs 0)) | |
| 63 | ;; This should not be necessary, but on some systems, we get | 63 | (if (floatp secs) |
| 64 | ;; unkillable processes without this. | 64 | (let* ((integer (floor secs)) |
| 65 | ;; It may be a kernel bug, but that's not certain. | 65 | (fraction (floor (* 1000000 (- secs integer))))) |
| 66 | (defun timer-kill-emacs-hook () | 66 | (setq usecs fraction secs integer))) |
| 67 | (if timer-process | 67 | (let ((newusecs (+ (aref timer 3) usecs))) |
| 68 | (progn | 68 | (aset timer 3 (mod newusecs 1000000)) |
| 69 | (set-process-sentinel timer-process nil) | 69 | (setq secs (+ secs (/ newusecs 1000000)))) |
| 70 | (set-process-filter timer-process nil) | 70 | (let ((newlow (+ (aref timer 2) secs)) |
| 71 | (delete-process timer-process)))) | 71 | (newhigh (aref timer 1))) |
| 72 | (add-hook 'kill-emacs-hook 'timer-kill-emacs-hook) | 72 | (setq newhigh (+ newhigh (/ newlow 65536)) |
| 73 | newlow (logand newlow 65535)) | ||
| 74 | (aset timer 1 newhigh) | ||
| 75 | (aset timer 2 newlow))) | ||
| 76 | |||
| 77 | (defun timer-set-time-with-usecs (timer time usecs &optional delta) | ||
| 78 | "Set the trigger time of TIMER to TIME. | ||
| 79 | TIME must be in the internal format returned by, e.g., `current-time' | ||
| 80 | If optional third argument DELTA is a non-zero integer make the timer | ||
| 81 | fire repeatedly that menu seconds apart." | ||
| 82 | (or (timerp timer) | ||
| 83 | (error "Invalid timer")) | ||
| 84 | (aset timer 1 (car time)) | ||
| 85 | (aset timer 2 (if (consp (cdr time)) (car (cdr time)) (cdr time))) | ||
| 86 | (aset timer 3 usecs) | ||
| 87 | (aset timer 4 (and (integerp delta) (> delta 0) delta)) | ||
| 88 | timer) | ||
| 89 | |||
| 90 | (defun timer-set-function (timer function &optional args) | ||
| 91 | "Make TIMER call FUNCTION with optional ARGS when triggering." | ||
| 92 | (or (timerp timer) | ||
| 93 | (error "Invalid timer")) | ||
| 94 | (aset timer 5 function) | ||
| 95 | (aset timer 6 args) | ||
| 96 | timer) | ||
| 97 | |||
| 98 | (defun timer-activate (timer) | ||
| 99 | "Put TIMER on the list of active timers." | ||
| 100 | (if (and (timerp timer) | ||
| 101 | (integerp (aref timer 1)) | ||
| 102 | (integerp (aref timer 2)) | ||
| 103 | (integerp (aref timer 3)) | ||
| 104 | (aref timer 5)) | ||
| 105 | (let ((timers timer-list) | ||
| 106 | last) | ||
| 107 | ;; Skip all timers to trigger before the new one. | ||
| 108 | (while (and timers | ||
| 109 | (or (> (aref timer 1) (aref (car timers) 1)) | ||
| 110 | (and (= (aref timer 1) (aref (car timers) 1)) | ||
| 111 | (> (aref timer 2) (aref (car timers) 2))) | ||
| 112 | (and (= (aref timer 1) (aref (car timers) 1)) | ||
| 113 | (= (aref timer 2) (aref (car timers) 2)) | ||
| 114 | (> (aref timer 3) (aref (car timers) 3))))) | ||
| 115 | (setq last timers | ||
| 116 | timers (cdr timers))) | ||
| 117 | ;; Insert new timer after last which possibly means in front of queue. | ||
| 118 | (if last | ||
| 119 | (setcdr last (cons timer timers)) | ||
| 120 | (setq timer-list (cons timer timers))) | ||
| 121 | (aset timer 0 nil) | ||
| 122 | nil) | ||
| 123 | (error "Invalid or uninitialized timer"))) | ||
| 124 | |||
| 125 | (defun cancel-timer (timer) | ||
| 126 | "Remove TIMER from the list of active timers." | ||
| 127 | (or (timerp timer) | ||
| 128 | (error "Invalid timer")) | ||
| 129 | (setq timer-list (delq timer timer-list)) | ||
| 130 | nil) | ||
| 73 | 131 | ||
| 132 | (defun cancel-function-timers (function) | ||
| 133 | "Cancel all timers scheduled by `run-at-time' which would run FUNCTION." | ||
| 134 | (interactive "aCancel timers of function: ") | ||
| 135 | (let ((tail timer-list)) | ||
| 136 | (while tail | ||
| 137 | (if (eq (aref (car tail) 5) function) | ||
| 138 | (setq timer-list (delq (car tail) timer-list))) | ||
| 139 | (setq tail (cdr tail))))) | ||
| 140 | |||
| 141 | ;; Set up the common handler for all timer events. Since the event has | ||
| 142 | ;; the timer as parameter we can still distinguish. Note that using | ||
| 143 | ;; special-event-map ensures that event timer events that arrive in the | ||
| 144 | ;; middle of a key sequence being entered are still handled correctly. | ||
| 145 | (define-key special-event-map [timer-event] 'timer-event-handler) | ||
| 146 | (defun timer-event-handler (event) | ||
| 147 | "Call the handler for the timer in the event EVENT." | ||
| 148 | (interactive "e") | ||
| 149 | (let ((timer (cdr-safe event))) | ||
| 150 | (if (timerp timer) | ||
| 151 | (progn | ||
| 152 | ;; Delete from queue. | ||
| 153 | (cancel-timer timer) | ||
| 154 | ;; Run handler | ||
| 155 | (apply (aref timer 5) (aref timer 6)) | ||
| 156 | ;; Re-schedule if requested. | ||
| 157 | (if (aref timer 4) | ||
| 158 | (progn | ||
| 159 | (timer-inc-time timer (aref timer 4) 0) | ||
| 160 | (timer-activate timer)))) | ||
| 161 | (error "Bogus timer event")))) | ||
| 162 | |||
| 74 | ;;;###autoload | 163 | ;;;###autoload |
| 75 | (defun run-at-time (time repeat function &rest args) | 164 | (defun run-at-time (time repeat function &rest args) |
| 76 | "Run a function at a time, and optionally on a regular interval. | 165 | "Run a function at a time, and optionally on a regular interval. |
| 77 | Arguments are TIME, REPEAT, FUNCTION &rest ARGS. | 166 | Arguments are TIME, REPEAT, FUNCTION &rest ARGS. |
| 78 | TIME, a string, can be specified absolutely or relative to now. | 167 | TIME is a string like \"11:23pm\" or a value from `encode-time'. |
| 79 | TIME can also be an integer, a number of seconds. | ||
| 80 | REPEAT, an integer number of seconds, is the interval on which to repeat | 168 | REPEAT, an integer number of seconds, is the interval on which to repeat |
| 81 | the call to the function. If REPEAT is nil or 0, call it just once. | 169 | the call to the function. If REPEAT is nil or 0, call it just once." |
| 82 | |||
| 83 | Absolute times may be specified in a wide variety of formats; | ||
| 84 | Something of the form `HOUR:MIN:SEC TIMEZONE MONTH/DAY/YEAR', where | ||
| 85 | all fields are numbers, works; the format used by the Unix `date' | ||
| 86 | command works too. | ||
| 87 | |||
| 88 | Relative times may be specified as a series of numbers followed by units: | ||
| 89 | 1 min denotes one minute from now. | ||
| 90 | min does too. | ||
| 91 | 1 min 5 sec denotes 65 seconds from now. | ||
| 92 | 1 min 2 sec 3 hour 4 day 5 week 6 fortnight 7 month 8 year | ||
| 93 | denotes the sum of all the given durations from now." | ||
| 94 | (interactive "sRun at time: \nNRepeat interval: \naFunction: ") | 170 | (interactive "sRun at time: \nNRepeat interval: \naFunction: ") |
| 95 | (if (equal repeat 0) | ||
| 96 | (setq repeat nil)) | ||
| 97 | ;; Make TIME a string. | ||
| 98 | (if (integerp time) | ||
| 99 | (setq time (format "%d sec" time))) | ||
| 100 | (cond ((or (not timer-process) | ||
| 101 | (memq (process-status timer-process) '(exit signal nil))) | ||
| 102 | (if timer-process (delete-process timer-process)) | ||
| 103 | (setq timer-process | ||
| 104 | (let ((process-connection-type nil)) | ||
| 105 | (start-process "timer" nil timer-program)) | ||
| 106 | timer-alist nil) | ||
| 107 | (set-process-filter timer-process 'timer-process-filter) | ||
| 108 | (set-process-sentinel timer-process 'timer-process-sentinel) | ||
| 109 | (process-kill-without-query timer-process)) | ||
| 110 | ((eq (process-status timer-process) 'stop) | ||
| 111 | (continue-process timer-process))) | ||
| 112 | ;; There should be a living, breathing timer process now | ||
| 113 | (let* ((token (concat (current-time-string) "-" (length timer-alist))) | ||
| 114 | (elt (list token repeat function args))) | ||
| 115 | (process-send-string timer-process (concat time "@" token "\n")) | ||
| 116 | (setq timer-alist (cons elt timer-alist)) | ||
| 117 | elt)) | ||
| 118 | |||
| 119 | (defun cancel-timer (elt) | ||
| 120 | "Cancel a timer previously made with `run-at-time'. | ||
| 121 | The argument should be a value previously returned by `run-at-time'. | ||
| 122 | Cancelling the timer means that nothing special | ||
| 123 | will happen at the specified time." | ||
| 124 | (setcar (cdr elt) nil) | ||
| 125 | (setcar (cdr (cdr elt)) 'ignore)) | ||
| 126 | |||
| 127 | (defun timer-process-filter (proc str) | ||
| 128 | (setq timer-out (concat timer-out str)) | ||
| 129 | (let (do token error) | ||
| 130 | (while (string-match "\n" timer-out) | ||
| 131 | (setq token (substring timer-out 0 (match-beginning 0)) | ||
| 132 | do (assoc token timer-alist) | ||
| 133 | timer-out (substring timer-out (match-end 0))) | ||
| 134 | (cond | ||
| 135 | (do | ||
| 136 | (apply (nth 2 do) (nth 3 do)) ; do it | ||
| 137 | (if (natnump (nth 1 do)) ; reschedule it | ||
| 138 | (send-string proc (concat (nth 1 do) " sec@" (car do) "\n")) | ||
| 139 | (setq timer-alist (delq do timer-alist)))) | ||
| 140 | ((string-match "timer: \\([^:]+\\): \\([^@]*\\)@\\(.*\\)$" token) | ||
| 141 | (setq error (substring token (match-beginning 1) (match-end 1)) | ||
| 142 | do (substring token (match-beginning 2) (match-end 2)) | ||
| 143 | token (assoc (substring token (match-beginning 3) (match-end 3)) | ||
| 144 | timer-alist) | ||
| 145 | timer-alist (delq token timer-alist)) | ||
| 146 | (or timer-alist | ||
| 147 | timer-dont-exit | ||
| 148 | (process-send-eof proc)) | ||
| 149 | ;; Update error message for this particular instance | ||
| 150 | (put 'timer-filter-error | ||
| 151 | 'error-message | ||
| 152 | (format "%s for %s; couldn't set at \"%s\"" | ||
| 153 | error (nth 2 token) do)) | ||
| 154 | (signal 'timer-filter-error (list proc str))))) | ||
| 155 | (or timer-alist timer-dont-exit (process-send-eof proc)))) | ||
| 156 | |||
| 157 | (defun timer-process-sentinel (proc str) | ||
| 158 | (let ((stat (process-status proc))) | ||
| 159 | (if (eq stat 'stop) | ||
| 160 | (continue-process proc) | ||
| 161 | ;; if it exited normally, presumably it was intentional. | ||
| 162 | ;; if there were no pending events, who cares that it exited? | ||
| 163 | (or (null timer-alist) | ||
| 164 | (eq stat 'exit) | ||
| 165 | (let ((alist timer-alist)) | ||
| 166 | (setq timer-process nil timer-alist nil) | ||
| 167 | (signal 'timer-abnormal-termination (list proc stat str alist)))) | ||
| 168 | ;; Used to set timer-scratch to "", but nothing uses that var. | ||
| 169 | (setq timer-process nil timer-alist nil)))) | ||
| 170 | |||
| 171 | (defun cancel-function-timers (function) | ||
| 172 | "Cancel all events scheduled by `run-at-time' which would run FUNCTION." | ||
| 173 | (interactive "aCancel timers of function: ") | ||
| 174 | (let ((alist timer-alist)) | ||
| 175 | (while alist | ||
| 176 | (if (eq (nth 2 (car alist)) function) | ||
| 177 | (setq timer-alist (delq (car alist) timer-alist))) | ||
| 178 | (setq alist (cdr alist)))) | ||
| 179 | (or timer-alist timer-dont-exit (process-send-eof timer-process))) | ||
| 180 | 171 | ||
| 181 | (provide 'timer) | 172 | ;; Handle "11:23pm" and the like. Interpret it as meaning today |
| 182 | 173 | ;; which admittedly is rather stupid if we have passed that time | |
| 183 | ;;; timer.el ends here | 174 | ;; already. Unfortunately we don't have a `parse-time' function |
| 175 | ;; to do the right thing. | ||
| 176 | (if (stringp time) | ||
| 177 | (progn | ||
| 178 | (require 'diary-lib) | ||
| 179 | (let ((hhmm (diary-entry-time time)) | ||
| 180 | (now (decode-time))) | ||
| 181 | (if (< hhmm 0) | ||
| 182 | (setq time 'bad) | ||
| 183 | (setq time | ||
| 184 | (encode-time 0 (% hhmm 100) (/ hhmm 100) (nth 3 now) | ||
| 185 | (nth 4 now) (nth 5 now) (nth 8 now))))))) | ||
| 186 | |||
| 187 | ;; Special case: nil means "now" and is useful when repeting. | ||
| 188 | (if (null time) | ||
| 189 | (setq time (current-time))) | ||
| 190 | |||
| 191 | (or (consp time) | ||
| 192 | (error "Invalid time format")) | ||
| 193 | |||
| 194 | (or (null repeat) | ||
| 195 | (natnump repeat) | ||
| 196 | (error "Invalid repetition interval")) | ||
| 197 | |||
| 198 | (let ((timer (timer-create))) | ||
| 199 | (timer-set-time timer time repeat) | ||
| 200 | (timer-set-function timer function args) | ||
| 201 | (timer-activate timer))) | ||
| 202 | |||
| 203 | (defun run-after-delay (secs usecs repeat function &rest args) | ||
| 204 | "Perform an action after a delay of SECS seconds and USECS microseconds. | ||
| 205 | Repeat the action every REPEAT seconds, if REPEAT is non-nil. | ||
| 206 | The action is to call FUNCTION with arguments ARGS." | ||
| 207 | (interactive "sRun after delay (seconds): \nNRepeat interval: \naFunction: ") | ||
| 208 | |||
| 209 | (or (null repeat) | ||
| 210 | (natnump repeat) | ||
| 211 | (error "Invalid repetition interval")) | ||
| 212 | |||
| 213 | (let ((timer (timer-create))) | ||
| 214 | (timer-set-time timer (current-time)) | ||
| 215 | (timer-inc-time timer secs usecs) | ||
| 216 | (timer-set-function timer function args) | ||
| 217 | (timer-activate timer))) | ||
| 218 | |||
| 219 | (provide 'timers) | ||
| 220 | |||
| 221 | ;;; timers.el ends here | ||