comparison lisp/prim/itimer.el @ 100:4be1180a9e89 r20-1b2

Import from CVS: tag r20-1b2
author cvs
date Mon, 13 Aug 2007 09:15:11 +0200
parents 131b0175ea99
children a145efe76779
comparison
equal deleted inserted replaced
99:2d83cbd90d8d 100:4be1180a9e89
1 ;;; Interval timers for XEmacs 1 ;;; itimer.el -- Interval timers for XEmacs
2 ;;; Copyright (C) 1988, 1991, 1993 Kyle E. Jones 2
3 ;;; Modified 5 Feb 91 by Jamie Zawinski <jwz@lucid.com> for Lucid Emacs 3 ;; Copyright (C) 1988, 1991, 1993, 1997 Kyle E. Jones
4 ;;; And again, 15 Dec 93. 4
5 ;;; 5 ;; Author: Kyle Jones <kyle_jones@wonderworks.com>
6 ;; This file is part of XEmacs. 6 ;; Keywords: extensions
7
8 ;; This file is part of XEmacs
7 9
8 ;; XEmacs is free software; you can redistribute it and/or modify it 10 ;; XEmacs is free software; you can redistribute it and/or modify it
9 ;; under the terms of the GNU General Public License as published by 11 ;; under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2, or (at your option) 12 ;; the Free Software Foundation; either version 2, or (at your option)
11 ;; any later version. 13 ;; any later version.
13 ;; XEmacs is distributed in the hope that it will be useful, but 15 ;; XEmacs is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of 16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ;; General Public License for more details. 18 ;; General Public License for more details.
17 19
18 ;; You should have received a copy of the GNU General Public License 20 ;; A copy of the GNU General Public License can be obtained from this
19 ;; along with XEmacs; see the file COPYING. If not, write to the 21 ;; program's author (send electronic mail to kyle@uunet.uu.net) or
20 ;; Free Software Foundation, 59 Temple Place - Suite 330, 22 ;; from the Free Software Foundation, Inc., 59 Temple Place - Suite
21 ;; Boston, MA 02111-1307, USA. 23 ;; 330, Boston, MA 02111-1307, USA.
22 24
23 ;;; Synched up with: Not in FSF. 25 ;;; Synched up with: Not in FSF
24 26
25 ;;; 27 ;;; Commentary:
26 ;;; Send bug reports to kyle@uunet.uu.net. 28
27 29 ;; Send bug reports to kyle_jones@wonderworks.com
28 ;; The original v18 version of this file worked by having an external program 30
29 ;; wake up once a second to generate an interrupt for emacs; then an emacs 31 ;;; Code:
30 ;; process filter was used to schedule timers. 32
31 ;; 33 (provide 'itimer)
32 ;; This version works by associating with each timer a "timeout" object, 34
33 ;; since the XEmacs/Lucid Emacs event loop has the concept of timers built 35 ;; `itimer' feature means Emacs-Lisp programmers get:
34 ;; in to it. There is no single scheduler function; instead, each timer 36 ;; itimerp
35 ;; re-sets itself as it is invoked. 37 ;; itimer-value
36 38 ;; itimer-restart
37 ;; `itimer' feature means Emacs-Lisp programers get: 39 ;; itimer-function
38 ;; itimerp, itimer-value, itimer-restart, itimer-function, 40 ;; itimer-function-argument
39 ;; set-itimer-value, set-itimer-restart, set-itimer-function 41 ;; set-itimer-value
40 ;; get-itimer, start-itimer, read-itimer, delete-itimer 42 ;; set-itimer-restart
43 ;; set-itimer-function
44 ;; set-itimer-uses-argument
45 ;; set-itimer-function-argument
46 ;; get-itimer
47 ;; start-itimer
48 ;; read-itimer
49 ;; delete-itimer
41 ;; 50 ;;
42 ;; Interactive users get these commands: 51 ;; Interactive users get these commands:
43 ;; edit-itimers, list-itimers, start-itimer 52 ;; edit-itimers
53 ;; list-itimers
54 ;; start-itimer
44 ;; 55 ;;
45 ;; See the doc strings of these functions for more information. 56 ;; See the doc strings of these functions for more information.
46 57
47 (defvar itimer-version "1.00" 58 (defvar itimer-version "1.01"
48 "Version number of the itimer package.") 59 "Version number of the itimer package.")
49 60
50 (defvar itimer-list nil 61 (defvar itimer-list nil
51 "List of all active itimers.") 62 "List of all active itimers.")
52 63
53 ;; not needed in XEmacs 64 (defvar itimer-process nil
54 ;(defvar itimer-process nil 65 "Process that drives all itimers, if a subprocess is being used.")
55 ; "Process that drives all itimers.") 66
56 67 (defvar itimer-timer nil
57 ;; This value is maintained internally; it does not determine itimer 68 "Emacs internal timer that drives the itimer system, if a subprocess
58 ;; granularity. Itimer granularity is 1 second, plus delays due to 69 is not being used to drive the system.")
59 ;; system and Emacs internal activity that delay dealing with process 70
60 ;; output. 71 (defvar itimer-timer-last-wakeup nil
61 ;; not needed in XEmacs 72 "The time the timer driver function last ran.")
62 ;(defvar itimer-process-next-wakeup 1 73
63 ; "Itimer process will wakeup to service running itimers within this 74 (defvar itimer-short-interval (if (featurep 'lisp-float-type) 1e-3 1)
64 ;many seconds.") 75 "Interval used for scheduling an event a very short time in the future.
76 Used internally to make the scheduler wake up early.
77 Unit is seconds.")
78
79 ;; This value is maintained internally; it does not determine
80 ;; itimer granularity. Itimer granularity is 1 second if your
81 ;; Emacs doens't support floats or your system doesn't have a
82 ;; clock with microsecond granularity. Otherwise granularity is
83 ;; to the microsend, although you can't possibly get timers to be
84 ;; executed with this kind of accuracy in practice. There will
85 ;; be delays due to system and Emacs internal activity that delay
86 ;; dealing with syunchronous events and process output.
87 (defvar itimer-next-wakeup itimer-short-interval
88 "Itimer process will wakeup to service running itimers within this
89 many seconds.")
65 90
66 (defvar itimer-edit-map nil 91 (defvar itimer-edit-map nil
67 "Keymap used when in Itimer Edit mode.") 92 "Keymap used when in Itimer Edit mode.")
68 93
69 (if itimer-edit-map 94 (if itimer-edit-map
83 (defvar itimer-edit-start-marker nil) 108 (defvar itimer-edit-start-marker nil)
84 109
85 ;; macros must come first... or byte-compile'd code will throw back its 110 ;; macros must come first... or byte-compile'd code will throw back its
86 ;; head and scream. 111 ;; head and scream.
87 112
88 (defmacro itimer-decf (variable) 113 (defmacro itimer-decrement (variable)
89 (list 'setq variable (list '1- variable))) 114 (list 'setq variable (list '1- variable)))
90 115
91 (defmacro itimer-incf (variable) 116 (defmacro itimer-increment (variable)
92 (list 'setq variable (list '1+ variable))) 117 (list 'setq variable (list '1+ variable)))
93 118
94 (defmacro itimer-signum (n) 119 (defmacro itimer-signum (n)
95 (list 'if (list '> n 0) 1 120 (list 'if (list '> n 0) 1
96 (list 'if (list 'zerop n) 0 -1))) 121 (list 'if (list 'zerop n) 0 -1)))
116 (list (list 'itimerp var) var) 141 (list (list 'itimerp var) var)
117 (list (list 'stringp var) (list 'get-itimer var)) 142 (list (list 'stringp var) (list 'get-itimer var))
118 (list t (list 'signal ''wrong-type-argument 143 (list t (list 'signal ''wrong-type-argument
119 (list 'list ''string-or-itimer-p var)))))) 144 (list 'list ''string-or-itimer-p var))))))
120 145
121 (defmacro itimer-check-natnum (var) 146 (defmacro check-nonnegative-number (var)
122 "If VAR is not bound to a non-negative number, signal wrong-type-argument. 147 "If VAR is not bound to a number, signal wrong-type-argument.
148 If VAR is not bound to a positive number, signal args-out-of-range.
123 This is a macro." 149 This is a macro."
124 (list 'setq var 150 (list 'setq var
125 (list 'if (list 'natnump var) var 151 (list 'if (list 'not (list 'numberp var))
126 (list 'signal ''wrong-type-argument 152 (list 'signal ''wrong-type-argument
127 (list 'list ''natnump var))))) 153 (list 'list ''natnump var))
128 154 (list 'if (list '< var 0)
129 (defmacro itimer-check-string (var) 155 (list 'signal ''args-out-of-range (list 'list var))
156 var))))
157
158 (defmacro check-string (var)
130 "If VAR is not bound to a string, signal wrong-type-argument. 159 "If VAR is not bound to a string, signal wrong-type-argument.
131 This is a macro." 160 This is a macro."
132 (list 'setq var 161 (list 'setq var
133 (list 'if (list 'stringp var) var 162 (list 'if (list 'stringp var) var
134 (list 'signal ''wrong-type-argument 163 (list 'signal ''wrong-type-argument
136 165
137 ;; Functions to access and modify itimer attributes. 166 ;; Functions to access and modify itimer attributes.
138 167
139 (defun itimerp (obj) 168 (defun itimerp (obj)
140 "Returns non-nil iff OBJ is an itimer." 169 "Returns non-nil iff OBJ is an itimer."
141 (and (consp obj) (stringp (car obj)) (eq (length obj) 170 (and (consp obj) (stringp (car obj)) (eq (length obj) 6)))
142 5 ; for XEmacs
143 ;4 ; original version
144 )))
145 171
146 (defun itimer-name (itimer) 172 (defun itimer-name (itimer)
147 "Returns the name of ITIMER." 173 "Returns the name of ITIMER."
148 (check-itimer itimer) 174 (check-itimer itimer)
149 (car itimer)) 175 (car itimer))
163 "Returns the function of ITIMER. 189 "Returns the function of ITIMER.
164 This function is called each time ITIMER expires." 190 This function is called each time ITIMER expires."
165 (check-itimer itimer) 191 (check-itimer itimer)
166 (nth 3 itimer)) 192 (nth 3 itimer))
167 193
168 ;; XEmacs-specific 194 (defun itimer-uses-argument (itimer)
169 (defun itimer-id (itimer) 195 "Returns non-nil if the function of ITIMER will be called with an argment.
170 "Returns the timeout-id of ITIMER." 196 ITIMER's function is called with this argument each timer ITIMER expires."
171 (check-itimer itimer) 197 (check-itimer itimer)
172 (nth 4 itimer)) 198 (nth 4 itimer))
173 199
174 (defun set-itimer-value (itimer value 200 (defun itimer-function-argument (itimer)
175 ;; XEmacs doesn't need this 201 "Returns the function argument of ITIMER.
176 ;; &optional nowakeup 202 ITIMER's function is called with this argument each timer ITIMER expires."
177 ) 203 (check-itimer itimer)
204 (nth 5 itimer))
205
206 (defun set-itimer-value (itimer value)
178 "Set the timeout value of ITIMER to be VALUE. 207 "Set the timeout value of ITIMER to be VALUE.
179 Itimer will expire is this many seconds. 208 Itimer will expire is this many seconds.
209 If your version of Emacs supports floating point numbers then
210 VALUE can be a floating point number. Otherwise it
211 must be an integer.
180 Returns VALUE." 212 Returns VALUE."
181 ;; Optional third arg NOWAKEUP non-nil means do not wakeup the itimer 213 (check-itimer itimer)
182 ;; process to recompute a correct wakeup time, even if it means this 214 (check-nonnegative-number value)
183 ;; itimer will expire late. itimer-process-filter uses this option.
184 ;; This is not meant for ordinary usage, which is why it is not
185 ;; mentioned in the doc string.
186 (check-itimer itimer)
187 (itimer-check-natnum value)
188 (let ((inhibit-quit t)) 215 (let ((inhibit-quit t))
189 216 ;; If the itimer is in the active list, and under the new
190 ; ;; If we're allowed to wakeup the itimer process, 217 ;; timeout value would expire before we would normally
191 ; ;; and the itimer process's next wakeup needs to be recomputed, 218 ;; wakeup, wakeup now and recompute a new wakeup time.
192 ; ;; and the itimer is running, then we wakeup the itimer process. 219 (or (and (< value itimer-next-wakeup)
193 ; (or (and (not nowakeup) (< value itimer-process-next-wakeup) 220 (get-itimer (itimer-name itimer))
194 ; (get-itimer (itimer-name itimer)) 221 (progn (itimer-driver-wakeup)
195 ; (progn (itimer-process-wakeup) 222 (setcar (cdr itimer) value)
196 ; (setcar (cdr itimer) value) 223 (itimer-driver-wakeup)
197 ; (itimer-process-wakeup))) 224 t ))
198 ; (setcar (cdr itimer) value)) 225 (setcar (cdr itimer) value))
199
200 ;; the XEmacs way:
201 (if (itimer-id itimer)
202 (deactivate-itimer itimer))
203 (setcar (cdr itimer) value)
204 (activate-itimer itimer)
205
206 value)) 226 value))
227
228 ;; Same as set-itimer-value but does not wakeup the driver.
229 ;; Only should be used by the drivers when processing expired timers.
230 (defun set-itimer-value-internal (itimer value)
231 (check-itimer itimer)
232 (check-nonnegative-number value)
233 (setcar (cdr itimer) value))
207 234
208 (defun set-itimer-restart (itimer restart) 235 (defun set-itimer-restart (itimer restart)
209 "Set the restart value of ITIMER to be RESTART. 236 "Set the restart value of ITIMER to be RESTART.
210 If RESTART is nil, ITIMER will not restart when it expires. 237 If RESTART is nil, ITIMER will not restart when it expires.
238 If your version of Emacs supports floating point numbers then
239 RESTART can be a floating point number. Otherwise it
240 must be an integer.
211 Returns RESTART." 241 Returns RESTART."
212 (check-itimer itimer) 242 (check-itimer itimer)
213 (if restart (itimer-check-natnum restart)) 243 (if restart (check-nonnegative-number restart))
214 (and restart (< restart 1) (signal 'args-out-of-range (list restart))) 244 (setcar (cdr (cdr itimer)) restart))
215 ;; (setcar (cdr (cdr itimer)) restart)
216 ;; the XEmacs way
217 (let ((was-active (itimer-id itimer))
218 (inhibit-quit t))
219 (if was-active
220 (deactivate-itimer itimer))
221 (setcar (cdr (cdr itimer)) restart)
222 (if was-active
223 (progn
224 (setcar (cdr itimer) restart)
225 (if restart
226 (activate-itimer itimer)))))
227 restart)
228 245
229 (defun set-itimer-function (itimer function) 246 (defun set-itimer-function (itimer function)
230 "Set the function of ITIMER to be FUNCTION. 247 "Set the function of ITIMER to be FUNCTION.
231 FUNCTION will be called when itimer expires. 248 FUNCTION will be called when itimer expires.
232 Returns FUNCTION." 249 Returns FUNCTION."
233 (check-itimer itimer) 250 (check-itimer itimer)
234 (setcar (cdr (cdr (cdr itimer))) function)) 251 (setcar (cdr (cdr (cdr itimer))) function))
235 252
236 ;; XEmacs-specific 253 (defun set-itimer-uses-argument (itimer flag)
237 (defun set-itimer-id (itimer id) 254 "Sets when the function of ITIMER is called with an argument.
238 (check-itimer itimer) 255 If FLAG is non-nil, then the function will be called with one argument,
239 (setcar (cdr (cdr (cdr (cdr itimer)))) id)) 256 otherwise the function will be called with no arguments.
257 Returns FLAG."
258 (check-itimer itimer)
259 (setcar (nthcdr 4 itimer) flag))
260
261 (defun set-itimer-function-argument (itimer argument)
262 "Set the function of ITIMER to be ARGUMENT.
263 The function of ITIMER will be called with ARGUMENT as its solt argument
264 when itimer expires.
265 Returns ARGUMENT."
266 (check-itimer itimer)
267 (setcar (nthcdr 5 itimer) argument))
240 268
241 (defun get-itimer (name) 269 (defun get-itimer (name)
242 "Return itimer named NAME, or nil if there is none." 270 "Return itimer named NAME, or nil if there is none."
243 (itimer-check-string name) 271 (check-string name)
244 (assoc name itimer-list)) 272 (assoc name itimer-list))
245 273
246 (defun read-itimer (prompt &optional initial-input) 274 (defun read-itimer (prompt &optional initial-input)
247 "Read the name of an itimer from the minibuffer and return the itimer 275 "Read the name of an itimer from the minibuffer and return the itimer
248 associated with that name. The user is prompted with PROMPT. 276 associated with that name. The user is prompted with PROMPT.
251 (get-itimer (completing-read prompt itimer-list nil 'confirm initial-input))) 279 (get-itimer (completing-read prompt itimer-list nil 'confirm initial-input)))
252 280
253 (defun delete-itimer (itimer) 281 (defun delete-itimer (itimer)
254 "Deletes ITIMER. ITIMER may be an itimer or the name of one." 282 "Deletes ITIMER. ITIMER may be an itimer or the name of one."
255 (check-itimer-coerce-string itimer) 283 (check-itimer-coerce-string itimer)
256 (deactivate-itimer itimer) ;; for XEmacs
257 (setq itimer-list (delq itimer itimer-list))) 284 (setq itimer-list (delq itimer itimer-list)))
258 285
259 ;jwz: this is preloaded so don't ;;;###autoload 286 (defun start-itimer (name function value &optional restart
260 (defun start-itimer (name function value &optional restart) 287 with-arg function-argument)
261 "Start an itimer. 288 "Start an itimer.
262 Args are NAME, FUNCTION, VALUE &optional RESTART. 289 Args are NAME, FUNCTION, VALUE &optional RESTART, WITH-ARG, FUNCTION-ARGUMENT.
263 NAME is an identifier for the itimer. It must be a string. If an itimer 290 NAME is an identifier for the itimer. It must be a string. If an itimer
264 already exists with this name, NAME will be modified slightly to until 291 already exists with this name, NAME will be modified slightly to until
265 it is unique. 292 it is unique.
266 FUNCTION should be a function (or symbol naming one) of no arguments. It 293 FUNCTION should be a function (or symbol naming one) of one argument. It
267 will be called each time the itimer expires. The function can access 294 will be called each time the itimer expires with an argument of
268 itimer that invoked it through the variable `current-itimer'. 295 FUNCTION-ARGUMENT. The function can access the itimer that
296 invoked it through the variable `current-itimer'. If WITH-ARG
297 is nil then FUNCTION is called with no arguments. This is for
298 backward compatibility with older versions of the itimer
299 package which always called FUNCTION with no arguments.
269 VALUE is the number of seconds until this itimer expires. 300 VALUE is the number of seconds until this itimer expires.
301 If your version of Emacs supports floating point numbers then
302 you can VALUE can be a floating point number. Otherwise it
303 must be an integer.
270 Optional fourth arg RESTART non-nil means that this itimer should be 304 Optional fourth arg RESTART non-nil means that this itimer should be
271 restarted automatically after its function is called. Normally an itimer 305 restarted automatically after its function is called. Normally an itimer
272 is deleted at expiration after its function has returned. 306 is deleted at expiration after its function has returned.
273 If non-nil RESTART should be a number indicating the value at which the 307 If non-nil RESTART should be a number indicating the value at which the
274 itimer should be set at restart time. 308 itimer should be set at restart time.
275 Returns the newly created itimer." 309 Returns the newly created itimer."
276 (interactive 310 (interactive
277 (list (completing-read "Start itimer: " itimer-list) 311 (list (completing-read "Start itimer: " itimer-list)
278 (read (completing-read "Itimer function: " obarray 'fboundp)) 312 (read (completing-read "Itimer function: " obarray 'fboundp))
279 (let (value) 313 (let (value)
280 (while (not (natnump value)) 314 (while (or (not (numberp value)) (< value 0))
281 (setq value (read-from-minibuffer "Itimer value: " nil nil t))) 315 (setq value (read-from-minibuffer "Itimer value: " nil nil t)))
282 value) 316 value)
283 (let ((restart t)) 317 (let ((restart t))
284 (while (and restart (not (natnump restart))) 318 (while (and restart (or (not (numberp restart)) (< restart 0)))
285 (setq restart (read-from-minibuffer "Itimer restart: " nil nil t))) 319 (setq restart (read-from-minibuffer "Itimer restart: "
286 restart))) 320 nil nil t)))
287 (itimer-check-string name) 321 restart)
288 (itimer-check-natnum value) 322 ;; hard to imagine the user specifying these interactively
289 (if restart (itimer-check-natnum restart)) 323 nil
324 nil ))
325 (check-string name)
326 (check-nonnegative-number value)
327 (if restart (check-nonnegative-number restart))
290 ;; Make proposed itimer name unique if it's not already. 328 ;; Make proposed itimer name unique if it's not already.
291 (let ((oname name) 329 (let ((oname name)
292 (num 2)) 330 (num 2))
293 (while (get-itimer name) 331 (while (get-itimer name)
294 (setq name (concat oname "<" num ">")) 332 (setq name (concat oname "<" num ">"))
295 (itimer-incf num))) 333 (itimer-increment num)))
296 ; ;; If there's no itimer process, start one now. 334 ;; If there's no itimer process, start one now.
297 ; ;; Otherwise wake up the itimer process so that seconds slept before 335 ;; Otherwise wake up the itimer process so that seconds slept before
298 ; ;; the new itimer is created won't be counted against it. 336 ;; the new itimer is created won't be counted against it.
299 ; (if itimer-process 337 (if (or itimer-process itimer-timer)
300 ; (itimer-process-wakeup) 338 (itimer-driver-wakeup)
301 ; (itimer-process-start)) 339 (itimer-driver-start))
302 (let ((inhibit-quit t)) 340 (let ((inhibit-quit t))
303 ;; add the itimer to the global list 341 ;; add the itimer to the global list
304 (setq itimer-list 342 (setq itimer-list
305 (cons (list name value restart function nil) ; extra slot for XEmacs 343 (cons (list name value restart function with-arg function-argument)
306 itimer-list)) 344 itimer-list))
307 ; ;; If the itimer process is scheduled to wake up too late for the itimer 345 ;; If the itimer process is scheduled to wake up too late for the itimer
308 ; ;; we wake it up to calculate a correct wakeup value giving consideration 346 ;; we wake it up to calculate a correct wakeup value giving consideration
309 ; ;; to the newly added itimer. 347 ;; to the newly added itimer.
310 ; (if (< value itimer-process-next-wakeup) 348 (if (< value itimer-next-wakeup)
311 ; (itimer-process-wakeup))) 349 (itimer-driver-wakeup)))
312 ;; for XEmacs
313 (activate-itimer (car itimer-list))
314 )
315 (car itimer-list)) 350 (car itimer-list))
316 351
317 ;; User level functions to list and modify existing itimers. 352 ;; User level functions to list and modify existing itimers.
318 ;; Itimer Edit major mode, and the editing commands thereof. 353 ;; Itimer Edit major mode, and the editing commands thereof.
319 354
329 (itimers (reverse itimer-list))) 364 (itimers (reverse itimer-list)))
330 (set-buffer buf) 365 (set-buffer buf)
331 (itimer-edit-mode) 366 (itimer-edit-mode)
332 (setq buffer-read-only nil) 367 (setq buffer-read-only nil)
333 (erase-buffer) 368 (erase-buffer)
334 (insert "Name Value Restart Function\n" 369 (insert
335 "---- ----- ------- --------") 370 "Name Value Restart Function Argument\n"
371 "---- ----- ------- -------- --------")
336 (if (null itimer-edit-start-marker) 372 (if (null itimer-edit-start-marker)
337 (setq itimer-edit-start-marker (point))) 373 (setq itimer-edit-start-marker (point)))
338 (while itimers 374 (while itimers
339 (newline 1) 375 (newline 1)
340 (prin1 (itimer-name (car itimers))) 376 (prin1 (itimer-name (car itimers)))
341 (tab-to-tab-stop) 377 (tab-to-tab-stop)
342 (prin1 (itimer-value (car itimers))) 378 (insert (itimer-truncate-string
379 (format "%5.5s" (itimer-value (car itimers))) 5))
343 (tab-to-tab-stop) 380 (tab-to-tab-stop)
344 (prin1 (itimer-restart (car itimers))) 381 (insert (itimer-truncate-string
382 (format "%5.5s" (itimer-restart (car itimers))) 5))
345 (tab-to-tab-stop) 383 (tab-to-tab-stop)
346 (prin1 (itimer-function (car itimers))) 384 (insert (itimer-truncate-string
385 (format "%.26s" (itimer-function (car itimers))) 26))
386 (tab-to-tab-stop)
387 (if (itimer-uses-argument (car itimers))
388 (prin1 (itimer-function-argument (car itimers)))
389 (prin1 'NONE))
347 (setq itimers (cdr itimers))) 390 (setq itimers (cdr itimers)))
348 ;; restore point 391 ;; restore point
349 (goto-char opoint) 392 (goto-char opoint)
350 (if (< (point) itimer-edit-start-marker) 393 (if (< (point) itimer-edit-start-marker)
351 (goto-char itimer-edit-start-marker)) 394 (goto-char itimer-edit-start-marker))
357 The major mode of the buffer containing the listing is Itimer Edit mode. 400 The major mode of the buffer containing the listing is Itimer Edit mode.
358 This major mode provides commands to manipulate itimers; see the documentation 401 This major mode provides commands to manipulate itimers; see the documentation
359 for `itimer-edit-mode' for more information." 402 for `itimer-edit-mode' for more information."
360 (interactive) 403 (interactive)
361 ;; since user is editing, make sure displayed data is reasonably up-to-date 404 ;; since user is editing, make sure displayed data is reasonably up-to-date
362 ; (if itimer-process 405 (if (or itimer-process itimer-timer)
363 ; (itimer-process-wakeup)) 406 (itimer-driver-wakeup))
364 (list-itimers) 407 (list-itimers)
365 (select-window (get-buffer-window "*Itimer List*")) 408 (select-window (get-buffer-window "*Itimer List*"))
366 (goto-char itimer-edit-start-marker) 409 (goto-char itimer-edit-start-marker)
367 (if itimer-list 410 (if itimer-list
368 (progn 411 (progn
387 (kill-all-local-variables) 430 (kill-all-local-variables)
388 (make-local-variable 'tab-stop-list) 431 (make-local-variable 'tab-stop-list)
389 (setq major-mode 'itimer-edit-mode 432 (setq major-mode 'itimer-edit-mode
390 mode-name "Itimer Edit" 433 mode-name "Itimer Edit"
391 truncate-lines t 434 truncate-lines t
392 tab-stop-list '(22 32 42)) 435 tab-stop-list '(22 32 40 67))
393 (abbrev-mode 0) 436 (abbrev-mode 0)
394 (auto-fill-mode 0) 437 (auto-fill-mode 0)
395 (buffer-disable-undo (current-buffer)) 438 (buffer-flush-undo (current-buffer))
396 (use-local-map itimer-edit-map) 439 (use-local-map itimer-edit-map)
397 (and lisp-mode-syntax-table (set-syntax-table lisp-mode-syntax-table))) 440 (set-syntax-table emacs-lisp-mode-syntax-table))
398 441
399 (put 'itimer-edit-mode 'mode-class 'special) 442 (put 'itimer-edit-mode 'mode-class 'special)
400 443
401 (defun itimer-edit-help () 444 (defun itimer-edit-help ()
402 "Help function for Itimer Edit." 445 "Help function for Itimer Edit."
433 (n 0)) 476 (n 0))
434 ;; count the number of sexprs until we reach the cursor 477 ;; count the number of sexprs until we reach the cursor
435 ;; and use this info to determine which field the user 478 ;; and use this info to determine which field the user
436 ;; wants to modify. 479 ;; wants to modify.
437 (beginning-of-line) 480 (beginning-of-line)
438 (while (and (>= opoint (point)) (< n 4)) 481 (while (and (>= opoint (point)) (< n 5))
439 (forward-sexp 2) 482 (forward-sexp 2)
440 (backward-sexp) 483 (backward-sexp)
441 (itimer-incf n)) 484 (itimer-increment n))
442 (cond ((eq n 1) (error "Cannot change itimer name.")) 485 (cond ((eq n 1) (error "Cannot change itimer name."))
443 ((eq n 2) 'value) 486 ((eq n 2) 'value)
444 ((eq n 3) 'restart) 487 ((eq n 3) 'restart)
445 ((eq n 4) 'function))))) 488 ((eq n 4) 'function)
489 (t 'function-argument)))))
446 (cond ((eq field 'value) 490 (cond ((eq field 'value)
447 ;; XEmacs: rewritten for I18N3 snarfing 491 (let ((prompt "Set itimer value: "))
448 (while (not (natnump field-value)) 492 (while (not (natnump field-value))
449 (setq field-value (read-from-minibuffer "Set itimer value: " 493 (setq field-value (read-from-minibuffer prompt nil nil t)))))
450 nil nil t))))
451 ((eq field 'restart) 494 ((eq field 'restart)
452 (while (and field-value (not (natnump field-value))) 495 (let ((prompt "Set itimer restart: "))
453 (setq field-value (read-from-minibuffer "Set itimer restart: " 496 (while (and field-value (not (natnump field-value)))
454 nil nil t)))) 497 (setq field-value (read-from-minibuffer prompt nil nil t)))))
455 ((eq field 'function) 498 ((eq field 'function)
456 (while (not (or (and (symbolp field-value) (fboundp field-value)) 499 (let ((prompt "Set itimer function: "))
457 (and (consp field-value) 500 (while (not (or (and (symbolp field-value) (fboundp field-value))
458 (memq (car field-value) '(lambda macro))))) 501 (and (consp field-value)
459 (setq field-value 502 (memq (car field-value) '(lambda macro)))))
460 (read (completing-read "Set itimer function: " 503 (setq field-value
461 obarray 'fboundp nil)))))) 504 (read (completing-read prompt obarray 'fboundp nil))))))
505 ((eq field 'function-argument)
506 (let ((prompt "Set itimer function argument: "))
507 (setq field-value (read-expression prompt))
508 (set-itimer-uses-argument itimer t))))
462 ;; set the itimer field 509 ;; set the itimer field
463 (funcall (intern (concat "set-itimer-" (symbol-name field))) 510 (funcall (intern (concat "set-itimer-" (symbol-name field)))
464 itimer field-value) 511 itimer field-value)
465 ;; move to beginning of field to be changed 512 ;; move to beginning of field to be changed
466 (itimer-edit-beginning-of-field) 513 (itimer-edit-beginning-of-field)
501 ;; treat fields at beginning of line as if they weren't there. 548 ;; treat fields at beginning of line as if they weren't there.
502 (if (bolp) 549 (if (bolp)
503 (progn 550 (progn
504 (forward-sexp 2) 551 (forward-sexp 2)
505 (backward-sexp))) 552 (backward-sexp)))
506 (itimer-decf count))) 553 (itimer-decrement count)))
507 ((< (itimer-signum count) 0) 554 ((< (itimer-signum count) 0)
508 (while (not (zerop count)) 555 (while (not (zerop count))
509 (backward-sexp) 556 (backward-sexp)
510 ;; treat fields at beginning of line as if they weren't there. 557 ;; treat fields at beginning of line as if they weren't there.
511 (if (bolp) 558 (if (bolp)
513 ;; wrap from itimer-edit-start-marker to field at eob. 560 ;; wrap from itimer-edit-start-marker to field at eob.
514 (if (<= (point) itimer-edit-start-marker) 561 (if (<= (point) itimer-edit-start-marker)
515 (progn 562 (progn
516 (goto-char (point-max)) 563 (goto-char (point-max))
517 (backward-sexp))) 564 (backward-sexp)))
518 (itimer-incf count))))) 565 (itimer-increment count)))))
519 566
520 (defun itimer-edit-previous-field (count) 567 (defun itimer-edit-previous-field (count)
521 (interactive "p") 568 (interactive "p")
522 (itimer-edit-next-field (- count))) 569 (itimer-edit-next-field (- count)))
523 570
526 (back (save-excursion (backward-sexp) (point)))) 573 (back (save-excursion (backward-sexp) (point))))
527 (cond ((eq forw-back back) (backward-sexp)) 574 (cond ((eq forw-back back) (backward-sexp))
528 ((eq forw-back (point)) t) 575 ((eq forw-back (point)) t)
529 (t (backward-sexp))))) 576 (t (backward-sexp)))))
530 577
578 (defun itimer-truncate-string (str len)
579 (if (<= (length str) len)
580 str
581 (substring str 0 len)))
531 582
532 ;; internals of the itimer implementation. 583 ;; internals of the itimer implementation.
533 584
585 (defun itimer-run-expired-timers (time-elapsed)
586 (let ((itimers (copy-sequence itimer-list))
587 (itimer)
588 (next-wakeup 600)
589 ;; process filters can be hit by stray C-g's from the user,
590 ;; so we must protect this stuff appropriately.
591 ;; Quit's are allowed from within itimer functions, but we
592 ;; catch them and print a message.
593 (inhibit-quit t))
594 (setq next-wakeup 600)
595 (while itimers
596 (setq itimer (car itimers))
597 (set-itimer-value-internal itimer (max 0 (- (itimer-value itimer)
598 time-elapsed)))
599 (if (> (itimer-value itimer) 0)
600 (setq next-wakeup
601 (min next-wakeup (itimer-value itimer)))
602 ;; itimer has expired, we must call its function.
603 ;; protect our local vars from the itimer function.
604 ;; allow keyboard quit to occur, but catch and report it.
605 ;; provide the variable `current-itimer' in case the function
606 ;; is interested.
607 (condition-case condition-data
608 (save-match-data
609 (let* ((current-itimer itimer)
610 (quit-flag nil)
611 (inhibit-quit nil)
612 itimer itimers time-elapsed)
613 (if (itimer-uses-argument current-itimer)
614 (funcall (itimer-function current-itimer)
615 (itimer-function-argument current-itimer))
616 (funcall (itimer-function current-itimer)))))
617 (error (message "itimer \"%s\" signaled: %s" (itimer-name itimer)
618 (prin1-to-string condition-data)))
619 (quit (message "itimer \"%s\" quit" (itimer-name itimer))))
620 ;; restart the itimer if we should, otherwise delete it.
621 (if (null (itimer-restart itimer))
622 (delete-itimer itimer)
623 (set-itimer-value-internal itimer (itimer-restart itimer))
624 (setq next-wakeup (min next-wakeup (itimer-value itimer)))))
625 (setq itimers (cdr itimers)))
626 ;; if user is editing itimers, update displayed info
627 (if (eq major-mode 'itimer-edit-mode)
628 (list-itimers))
629 next-wakeup ))
630
534 (defun itimer-process-filter (process string) 631 (defun itimer-process-filter (process string)
535 (error "itimer-process-filter is not used in XEmacs") 632 ;; If the itimer process dies and generates output while doing
536 ; ;; If the itimer process dies and generates output while doing 633 ;; so, we may be called before the process-sentinel. Sanity
537 ; ;; so, we may be called before the process-sentinel. Sanity 634 ;; check the output just in case...
538 ; ;; check the output just in case... 635 (if (not (string-match "^[0-9]" string))
539 ; (if (not (string-match "^[0-9]" string)) 636 (progn (message "itimer process gave odd output: %s" string)
540 ; (progn (message "itimer process gave odd output: %s" string) 637 ;; it may be still alive and waiting for input
541 ; ;; it may be still alive and waiting for input 638 (process-send-string itimer-process "3\n"))
542 ; (process-send-string itimer-process "3\n")) 639 ;; if there are no active itimers, return quickly.
543 ; ;; if there are no active itimers, return quickly. 640 (if itimer-list
544 ; (if itimer-list 641 (setq itimer-next-wakeup
545 ; (let ((time-elapsed (string-to-int string)) 642 (itimer-run-expired-timers (string-to-int string)))
546 ; (itimers itimer-list) 643 (setq itimer-next-wakeup 600))
547 ; (itimer) 644 ;; tell itimer-process when to wakeup again
548 ; ;; process filters can be hit by stray C-g's from the user, 645 (process-send-string itimer-process
549 ; ;; so we must protect this stuff appropriately. 646 (concat (int-to-string itimer-next-wakeup)
550 ; ;; Quit's are allowed from within itimer functions, but we 647 "\n"))))
551 ; ;; catch them.
552 ; (inhibit-quit t))
553 ; (setq itimer-process-next-wakeup 600)
554 ; (while itimers
555 ; (setq itimer (car itimers))
556 ; (set-itimer-value itimer (max 0 (- (itimer-value itimer) time-elapsed)) t)
557 ; (if (> (itimer-value itimer) 0)
558 ; (setq itimer-process-next-wakeup
559 ; (min itimer-process-next-wakeup (itimer-value itimer)))
560 ; ;; itimer has expired, we must call its function.
561 ; ;; protect our local vars from the itimer function.
562 ; ;; allow keyboard quit to occur, but catch and report it.
563 ; ;; provide the variable `current-itimer' in case the function
564 ; ;; is interested.
565 ; (condition-case condition-data
566 ; (let* ((current-itimer itimer)
567 ; itimer itimers time-elapsed
568 ; quit-flag inhibit-quit)
569 ; (funcall (itimer-function current-itimer)))
570 ; (error (message "itimer \"%s\" signaled: %s" (itimer-name itimer)
571 ; (prin1-to-string condition-data)))
572 ; (quit (message "itimer \"%s\" quit" (itimer-name itimer))))
573 ; ;; restart the itimer if we should, otherwise delete it.
574 ; (if (null (itimer-restart itimer))
575 ; (delete-itimer itimer)
576 ; (set-itimer-value itimer (itimer-restart itimer) t)
577 ; (setq itimer-process-next-wakeup
578 ; (min itimer-process-next-wakeup (itimer-value itimer)))))
579 ; (setq itimers (cdr itimers)))
580 ; ;; if user is editing itimers, update displayed info
581 ; (if (eq major-mode 'itimer-edit-mode)
582 ; (list-itimers)))
583 ; (setq itimer-process-next-wakeup 600))
584 ; ;; tell itimer-process when to wakeup again
585 ; (process-send-string itimer-process
586 ; (concat (int-to-string itimer-process-next-wakeup)
587 ; "\n")))
588 )
589 648
590 (defun itimer-process-sentinel (process message) 649 (defun itimer-process-sentinel (process message)
591 (error "itimer-process-sentinel is not used in XEmacs") 650 (let ((inhibit-quit t))
592 ; (let ((inhibit-quit t)) 651 (if (eq (process-status process) 'stop)
593 ; (if (eq (process-status process) 'stop) 652 (continue-process process)
594 ; (continue-process process) 653 ;; not stopped, so it must have died.
595 ; ;; not stopped, so it must have died. 654 ;; cleanup first...
596 ; ;; cleanup first... 655 (delete-process process)
597 ; (delete-process process) 656 (setq itimer-process nil)
598 ; (setq itimer-process nil) 657 ;; now, if there are any active itimers then we need to immediately
599 ; ;; now, if there are any active itimers then we need to immediately 658 ;; start another itimer process, otherwise we can wait until the next
600 ; ;; start another itimer process, otherwise we can wait until the next 659 ;; start-itimer call, which will start one automatically.
601 ; ;; start-itimer call, which will start one automatically. 660 (if (null itimer-list)
602 ; (if (null itimer-list) 661 ()
603 ; () 662 ;; there may have been an error message in the echo area;
604 ; ;; there may have been an error message in the echo area; 663 ;; give the user at least a little time to read it.
605 ; ;; give the user at least a little time to read it. 664 (sit-for 2)
606 ; (sit-for 2) 665 (message "itimer process %s... respawning." (substring message 0 -1))
607 ; (message "itimer process %s... respawning." (substring message 0 -1)) 666 (itimer-process-start)))))
608 ; (itimer-process-start))))
609 )
610 667
611 (defun itimer-process-start () 668 (defun itimer-process-start ()
612 (error "itimer-process-start is not used in XEmacs") 669 (let ((inhibit-quit t)
613 ; (let ((inhibit-quit t) 670 (process-connection-type nil))
614 ; (process-connection-type nil)) 671 (setq itimer-process (start-process "itimer" nil "itimer"))
615 ; (setq itimer-process (start-process "itimer" nil "itimer")) 672 (process-kill-without-query itimer-process)
616 ; (process-kill-without-query itimer-process) 673 (set-process-filter itimer-process 'itimer-process-filter)
617 ; (set-process-filter itimer-process 'itimer-process-filter) 674 (set-process-sentinel itimer-process 'itimer-process-sentinel)
618 ; (set-process-sentinel itimer-process 'itimer-process-sentinel) 675 ;; Tell itimer process to wake up quickly, so that a correct
619 ; ;; Tell itimer process to wake up quickly, so that a correct wakeup 676 ;; wakeup time can be computed. Zero loses because of
620 ; ;; time can be computed. Zero instead of one here loses because of 677 ;; underlying itimer implementations that use 0 to mean
621 ; ;; underlying itimer implementations that use 0 to mean `disable the 678 ;; `disable the itimer'.
622 ; ;; itimer'. 679 (setq itimer-next-wakeup itimer-short-interval)
623 ; (setq itimer-process-next-wakeup 1) 680 (process-send-string itimer-process
624 ; (process-send-string itimer-process "1\n")) 681 (format "%s\n" itimer-next-wakeup))))
625 )
626 682
627 (defun itimer-process-wakeup () 683 (defun itimer-process-wakeup ()
628 (error "itimer-process-wakeup is not used in XEmacs") 684 (interrupt-process itimer-process)
629 ; (interrupt-process itimer-process) 685 (accept-process-output))
630 ; (accept-process-output) 686
631 ) 687 (defun itimer-timer-start ()
632
633
634 ;; XEmacs-specific code
635
636 (defun activate-itimer (itimer)
637 (let ((inhibit-quit t)) 688 (let ((inhibit-quit t))
638 (set-itimer-id itimer 689 (setq itimer-next-wakeup itimer-short-interval
639 (add-timeout (itimer-value itimer) 690 itimer-timer-last-wakeup (current-time)
640 'itimer-callback 691 itimer-timer (add-timeout itimer-short-interval
641 itimer 692 'itimer-timer-driver nil nil))))
642 (itimer-restart itimer)))) 693
643 itimer) 694 (defun itimer-timer-wakeup ()
644 695 (let ((inhibit-quit t))
645 (defun deactivate-itimer (itimer) 696 (cond ((fboundp 'cancel-timer)
646 (let ((inhibit-quit t) 697 (cancel-timer itimer-timer))
647 (id (itimer-id itimer))) 698 ((fboundp 'disable-timeout)
648 (and id (disable-timeout id)) 699 (disable-timeout itimer-timer)))
649 (set-itimer-id itimer nil)) 700 (setq itimer-timer (add-timeout itimer-short-interval
650 itimer) 701 'itimer-timer-driver nil nil))))
651 702
652 (defun itimer-callback (current-itimer) 703 (defun itimer-time-difference (t1 t2)
653 (funcall (itimer-function current-itimer))) 704 ;; ignore high 16 bits since we will never be dealing with
654 705 ;; times that long.
655 706 (setq t1 (cdr t1)
656 ;;; itimer-driven auto-saves 707 t2 (cdr t2))
657 708 (let ((usecs (- (nth 1 t1) (nth 1 t2)))
658 ;jwz: this is preloaded so don't ;;;###autoload 709 (secs (- (car t1) (car t2))))
659 (defvar auto-save-timeout 30 710 (if (< usecs 0)
660 "*Number of seconds idle time before auto-save. 711 (setq secs (1- secs)
661 Zero or nil means disable auto-saving due to idleness. 712 usecs (+ usecs 1000000)))
662 713 (+ secs (/ usecs (if (featurep 'lisp-float-type) 1e6 1000000)))))
663 The actual amount of idle time between auto-saves is logarithmically related 714
664 to the size of the current buffer. This variable is the number of seconds 715 (defun itimer-timer-driver (&rest ignored)
665 after which an auto-save will happen when the current buffer is 50k or less; 716 ;; inhibit quit because if the user quits at an inopportune
666 the timeout will be 2 1/4 times this in a 200k buffer, 3 3/4 times this in a 717 ;; time, the timer process won't bne launched again and the
667 1000k buffer, and 4 1/2 times this in a 2000k buffer. 718 ;; system stops working. itimer-run-expired-timers allows
668 719 ;; individual timer function to be aborted, so the user can
669 See also the variable `auto-save-interval', which controls auto-saving based 720 ;; escape a feral timer function.
670 on the number of characters typed.") 721 (let* ((inhibit-quit t)
671 722 (now (current-time))
672 ;jwz: this is preloaded so don't ;;;###autoload 723 (elapsed (itimer-time-difference now itimer-timer-last-wakeup))
673 (defvar auto-gc-threshold (/ gc-cons-threshold 3) 724 sleep)
674 "*GC when this many bytes have been consed since the last GC, 725 (setq itimer-timer-last-wakeup now
675 and the user has been idle for `auto-save-timeout' seconds.") 726 sleep (itimer-run-expired-timers elapsed)
676 727 itimer-next-wakeup sleep
677 (defun auto-save-itimer () 728 itimer-timer (add-timeout sleep 'itimer-timer-driver nil nil))))
678 "For use as a itimer callback function. 729
679 Auto-saves and garbage-collects based on the size of the current buffer 730 (defun itimer-driver-start ()
680 and the value of `auto-save-timeout', `auto-gc-threshold', and the current 731 (if (fboundp 'add-timeout)
681 keyboard idle-time." 732 (itimer-timer-start)
682 (if (or (null auto-save-timeout) 733 (itimer-process-start)))
683 (<= auto-save-timeout 0) 734
684 (eq (minibuffer-window) (selected-window))) 735 (defun itimer-driver-wakeup ()
685 nil 736 (if (fboundp 'add-timeout)
686 (let ((buf-size (1+ (ash (buffer-size) -8))) 737 (itimer-timer-wakeup)
687 (delay-level 0) 738 (itimer-process-wakeup)))
688 (now (current-time)) 739
689 delay) 740 ;;; itimer.el ends here
690 (while (> buf-size 64)
691 (setq delay-level (1+ delay-level)
692 buf-size (- buf-size (ash buf-size -2))))
693 (if (< delay-level 4)
694 (setq delay-level 4))
695 ;; delay_level is 4 for files under around 50k, 7 at 100k, 9 at 200k,
696 ;; 11 at 300k, and 12 at 500k, 15 at 1 meg, and 17 at 2 meg.
697 (setq delay (/ (* delay-level auto-save-timeout) 4))
698 (let ((idle-time (if (or (not (consp last-input-time))
699 (/= (car now) (car last-input-time)))
700 (1+ delay)
701 (- (car (cdr now)) (cdr last-input-time)))))
702 (and (> idle-time delay)
703 (do-auto-save))
704 (and (> idle-time auto-save-timeout)
705 (> (consing-since-gc) auto-gc-threshold)
706 (garbage-collect)))))
707 ;; Look at the itimer that's currently running; if the user has changed
708 ;; the value of auto-save-timeout, modify this itimer to have the correct
709 ;; restart time. There will be some latency between when the user changes
710 ;; this variable and when it takes effect, but it will happen eventually.
711 (let ((self (get-itimer "auto-save")))
712 (or self (error "auto-save-itimer can't find itself"))
713 (if (and auto-save-timeout (> auto-save-timeout 4))
714 (or (= (itimer-restart self) (/ auto-save-timeout 4))
715 (set-itimer-restart self (/ auto-save-timeout 4)))))
716 nil)
717
718 (defun itimer-init-auto-gc ()
719 (or noninteractive ; may be being run from after-init-hook in -batch mode.
720 (get-itimer "auto-save")
721 ;; the time here is just the first interval; if the user changes it
722 ;; later, it will adjust.
723 (let ((time (max 2 (/ (or auto-save-timeout 30) 4))))
724 (start-itimer "auto-save" 'auto-save-itimer time time))))
725
726 (cond (purify-flag
727 ;; This file is being preloaded into an emacs about to be dumped.
728 ;; So arrange for the auto-save itimer to be started once emacs
729 ;; is launched.
730 (add-hook 'after-init-hook 'itimer-init-auto-gc))
731 (t
732 ;; Otherwise, this file is being loaded into a normal, interactive
733 ;; emacs. Start the auto-save timer now.
734 (itimer-init-auto-gc)))
735
736
737 (provide 'itimer)