* lisp/emacs-lisp/cl-lib.el: Set more meaningful version number.
[bpt/emacs.git] / lisp / emacs-lisp / timer.el
index 5f06922..284c591 100644 (file)
@@ -1,6 +1,6 @@
 ;;; timer.el --- run a function with args at some time in future
 
-;; Copyright (C) 1996, 2001-2011  Free Software Foundation, Inc.
+;; Copyright (C) 1996, 2001-2012  Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Package: emacs
 
 ;; Layout of a timer vector:
 ;; [triggered-p high-seconds low-seconds usecs repeat-delay
-;;  function args idle-delay]
+;;  function args idle-delay psecs]
 ;; triggered-p is nil if the timer is active (waiting to be triggered),
 ;;  t if it is inactive ("already triggered", in theory)
 
-(eval-when-compile (require 'cl))
+(eval-when-compile (require 'cl-lib))
 
-(defstruct (timer
+(cl-defstruct (timer
             (:constructor nil)
             (:copier nil)
             (:constructor timer-create ())
             (:type vector)
             (:conc-name timer--))
   (triggered t)
-  high-seconds low-seconds usecs repeat-delay function args idle-delay)
+  high-seconds low-seconds usecs repeat-delay function args idle-delay psecs)
 
 (defun timerp (object)
   "Return t if OBJECT is a timer."
-  (and (vectorp object) (= (length object) 8)))
+  (and (vectorp object) (= (length object) 9)))
 
 ;; Pseudo field `time'.
 (defun timer--time (timer)
   (list (timer--high-seconds timer)
         (timer--low-seconds timer)
-        (timer--usecs timer)))
+       (timer--usecs timer)
+       (timer--psecs timer)))
 
-(defsetf timer--time
+(gv-define-simple-setter timer--time
   (lambda (timer time)
     (or (timerp timer) (error "Invalid timer"))
     (setf (timer--high-seconds timer) (pop time))
-    (setf (timer--low-seconds timer)
-         (if (consp time) (car time) time))
-    (setf (timer--usecs timer) (or (and (consp time) (consp (cdr time))
-                                       (cadr time))
-                                  0))))
+    (let ((low time) (usecs 0) (psecs 0))
+      (if (consp time)
+         (progn
+           (setq low (pop time))
+           (if time
+               (progn
+                 (setq usecs (pop time))
+                 (if time
+                     (setq psecs (car time)))))))
+      (setf (timer--low-seconds timer) low)
+      (setf (timer--usecs timer) usecs)
+      (setf (timer--psecs timer) psecs))))
 
 
 (defun timer-set-time (timer time &optional delta)
@@ -77,7 +85,7 @@ fire repeatedly that many seconds apart."
 (defun timer-set-idle-time (timer secs &optional repeat)
   "Set the trigger idle time of TIMER to SECS.
 SECS may be an integer, floating point number, or the internal
-time format (HIGH LOW USECS) returned by, e.g., `current-idle-time'.
+time format returned by, e.g., `current-idle-time'.
 If optional third argument REPEAT is non-nil, make the timer
 fire each time Emacs is idle for that many seconds."
   (if (consp secs)
@@ -91,63 +99,46 @@ fire each time Emacs is idle for that many seconds."
   "Yield the next value after TIME that is an integral multiple of SECS.
 More precisely, the next value, after TIME, that is an integral multiple
 of SECS seconds since the epoch.  SECS may be a fraction."
-  (let ((time-base (ash 1 16)))
-    ;; Use floating point, taking care to not lose precision.
-    (let* ((float-time-base (float time-base))
-          (million 1000000.0)
-          (time-usec (+ (* million
-                           (+ (* float-time-base (nth 0 time))
-                              (nth 1 time)))
-                        (nth 2 time)))
-          (secs-usec (* million secs))
-          (mod-usec (mod time-usec secs-usec))
-          (next-usec (+ (- time-usec mod-usec) secs-usec))
-          (time-base-million (* float-time-base million)))
-      (list (floor next-usec time-base-million)
-           (floor (mod next-usec time-base-million) million)
-           (floor (mod next-usec million))))))
-
-(defun timer-relative-time (time secs &optional usecs)
-  "Advance TIME by SECS seconds and optionally USECS microseconds.
-SECS may be either an integer or a floating point number."
-  ;; FIXME: we should just use (time-add time (list 0 secs usecs))
-  (let ((high (car time))
-       (low (if (consp (cdr time)) (nth 1 time) (cdr time)))
-       (micro (if (numberp (car-safe (cdr-safe (cdr time))))
-                  (nth 2 time)
-                0)))
-    ;; Add
-    (if usecs (setq micro (+ micro usecs)))
-    (if (floatp secs)
-       (setq micro (+ micro (floor (* 1000000 (- secs (floor secs)))))))
-    (setq low (+ low (floor secs)))
-
-    ;; Normalize
-    ;; `/' rounds towards zero while `mod' returns a positive number,
-    ;; so we can't rely on (= a (+ (* 100 (/ a 100)) (mod a 100))).
-    (setq low (+ low (/ micro 1000000) (if (< micro 0) -1 0)))
-    (setq micro (mod micro 1000000))
-    (setq high (+ high (/ low 65536) (if (< low 0) -1 0)))
-    (setq low (logand low 65535))
-
-    (list high low (and (/= micro 0) micro))))
+  (let* ((trillion 1e12)
+        (time-sec (+ (nth 1 time)
+                     (* 65536.0 (nth 0 time))))
+        (delta-sec (mod (- time-sec) secs))
+        (next-sec (+ time-sec (ffloor delta-sec)))
+        (next-sec-psec (ffloor (* trillion (mod delta-sec 1))))
+        (sub-time-psec (+ (or (nth 3 time) 0)
+                          (* 1e6 (nth 2 time))))
+        (psec-diff (- sub-time-psec next-sec-psec)))
+    (if (and (<= next-sec time-sec) (< 0 psec-diff))
+       (setq next-sec-psec (+ sub-time-psec
+                              (mod (- psec-diff) (* trillion secs)))))
+    (setq next-sec (+ next-sec (floor next-sec-psec trillion)))
+    (setq next-sec-psec (mod next-sec-psec trillion))
+    (list (floor next-sec 65536)
+         (floor (mod next-sec 65536))
+         (floor next-sec-psec 1000000)
+         (floor (mod next-sec-psec 1000000)))))
+
+(defun timer-relative-time (time secs &optional usecs psecs)
+  "Advance TIME by SECS seconds and optionally USECS nanoseconds
+and PSECS picoseconds.  SECS may be either an integer or a
+floating point number."
+  (let ((delta (if (floatp secs)
+                  (seconds-to-time secs)
+                (list (floor secs 65536) (mod secs 65536)))))
+    (if (or usecs psecs)
+       (setq delta (time-add delta (list 0 0 (or usecs 0) (or psecs 0)))))
+    (time-add time delta)))
 
 (defun timer--time-less-p (t1 t2)
   "Say whether time value T1 is less than time value T2."
-  ;; FIXME just use time-less-p.
-  (destructuring-bind (high1 low1 micro1) (timer--time t1)
-    (destructuring-bind (high2 low2 micro2) (timer--time t2)
-      (or (< high1 high2)
-          (and (= high1 high2)
-               (or (< low1 low2)
-                   (and (= low1 low2)
-                        (< micro1 micro2))))))))
-
-(defun timer-inc-time (timer secs &optional usecs)
-  "Increment the time set in TIMER by SECS seconds and USECS microseconds.
-SECS may be a fraction.  If USECS is omitted, that means it is zero."
+  (time-less-p (timer--time t1) (timer--time t2)))
+
+(defun timer-inc-time (timer secs &optional usecs psecs)
+  "Increment the time set in TIMER by SECS seconds, USECS nanoseconds,
+and PSECS picoseconds.  SECS may be a fraction.  If USECS or PSECS are
+omitted, they are treated as zero."
   (setf (timer--time timer)
-        (timer-relative-time (timer--time timer) secs usecs)))
+        (timer-relative-time (timer--time timer) secs usecs psecs)))
 
 (defun timer-set-time-with-usecs (timer time usecs &optional delta)
   "Set the trigger time of TIMER to TIME plus USECS.
@@ -155,13 +146,13 @@ TIME must be in the internal format returned by, e.g., `current-time'.
 The microsecond count from TIME is ignored, and USECS is used instead.
 If optional fourth argument DELTA is a positive number, make the timer
 fire repeatedly that many seconds apart."
+  (declare (obsolete "use `timer-set-time' and `timer-inc-time' instead."
+                    "22.1"))
   (setf (timer--time timer) time)
   (setf (timer--usecs timer) usecs)
+  (setf (timer--psecs timer) 0)
   (setf (timer--repeat-delay timer) (and (numberp delta) (> delta 0) delta))
   timer)
-(make-obsolete 'timer-set-time-with-usecs
-               "use `timer-set-time' and `timer-inc-time' instead."
-               "22.1")
 
 (defun timer-set-function (timer function &optional args)
   "Make TIMER call FUNCTION with optional ARGS when triggering."
@@ -176,6 +167,7 @@ fire repeatedly that many seconds apart."
           (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)
@@ -189,35 +181,42 @@ fire repeatedly that many seconds apart."
              (setcdr reuse-cell timers))
          (setq reuse-cell (cons timer timers)))
        ;; Insert new timer after last which possibly means in front of queue.
-       (if last
-           (setcdr last reuse-cell)
-          (if idle
-              (setq timer-idle-list reuse-cell)
-            (setq timer-list reuse-cell)))
+       (cond (last (setcdr last reuse-cell))
+             (idle (setq timer-idle-list reuse-cell))
+             (t    (setq timer-list reuse-cell)))
        (setf (timer--triggered timer) triggered-p)
        (setf (timer--idle-delay timer) idle)
        nil)
     (error "Invalid or uninitialized timer")))
 
-(defun timer-activate (timer &optional triggered-p reuse-cell idle)
-  "Put TIMER on the list of active timers.
-
-If TRIGGERED-P is t, that means to make the timer inactive
-\(put it on the list, but mark it as already triggered).
-To remove from the list, use `cancel-timer'.
+(defun timer-activate (timer &optional triggered-p reuse-cell)
+  "Insert TIMER into `timer-list'.
+If TRIGGERED-P is t, make TIMER inactive (put it on the list, but
+mark it as already triggered).  To remove it, use `cancel-timer'.
 
-REUSE-CELL, if non-nil, is a cons cell to reuse instead
-of allocating a new one."
+REUSE-CELL, if non-nil, is a cons cell to reuse when inserting
+TIMER into `timer-list' (usually a cell removed from that list by
+`cancel-timer-internal'; using this reduces consing for repeat
+timers).  If nil, allocate a new cell."
   (timer--activate timer triggered-p reuse-cell nil))
 
 (defun timer-activate-when-idle (timer &optional dont-wait reuse-cell)
-  "Arrange to activate TIMER whenever Emacs is next idle.
-If optional argument DONT-WAIT is non-nil, then enable the
-timer to activate immediately, or at the right time, if Emacs
-is already idle.
-
-REUSE-CELL, if non-nil, is a cons cell to reuse instead
-of allocating a new one."
+  "Insert TIMER into `timer-idle-list'.
+This arranges to activate TIMER whenever Emacs is next idle.
+If optional argument DONT-WAIT is non-nil, set TIMER to activate
+immediately \(see below\), or at the right time, if Emacs is
+already idle.
+
+REUSE-CELL, if non-nil, is a cons cell to reuse when inserting
+TIMER into `timer-idle-list' (usually a cell removed from that
+list by `cancel-timer-internal'; using this reduces consing for
+repeat timers).  If nil, allocate a new cell.
+
+Using non-nil DONT-WAIT is not recommended when activating an
+idle timer from an idle timer handler, if the timer being
+activated has an idleness time that is smaller or equal to
+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)
@@ -262,21 +261,20 @@ and idle timers such as are scheduled by `run-with-idle-timer'."
 (defvar timer-event-last-2 nil
   "Third-to-last timer that was run.")
 
-(defvar timer-max-repeats 10
-  "*Maximum number of times to repeat a timer, if many repeats are delayed.
+(defcustom timer-max-repeats 10
+  "Maximum number of times to repeat a timer, if many repeats are delayed.
 Timer invocations can be delayed because Emacs is suspended or busy,
 or because the system's time changes.  If such an occurrence makes it
 appear that many invocations are overdue, this variable controls
-how many will really happen.")
+how many will really happen."
+  :type 'integer
+  :group 'internal)
 
 (defun timer-until (timer time)
   "Calculate number of seconds from when TIMER will run, until TIME.
 TIMER is a timer, and stands for the time when its next repeat is scheduled.
 TIME is a time-list."
-  ;; FIXME: (float-time (time-subtract (timer--time timer) time))
-  (let ((high (- (car time) (timer--high-seconds timer)))
-       (low (- (nth 1 time) (timer--low-seconds timer))))
-    (+ low (* high 65536))))
+  (- (float-time time) (float-time (timer--time timer))))
 
 (defun timer-event-handler (timer)
   "Call the handler for the timer TIMER.
@@ -409,9 +407,11 @@ This function is for compatibility; see also `run-with-timer'."
   "Perform an action the next time Emacs is idle for SECS seconds.
 The action is to call FUNCTION with arguments ARGS.
 SECS may be an integer, a floating point number, or the internal
-time format (HIGH LOW USECS) returned by, e.g., `current-idle-time'.
+time format returned by, e.g., `current-idle-time'.
 If Emacs is currently idle, and has been idle for N seconds (N < SECS),
-then it will call FUNCTION in SECS - N seconds from now.
+then it will call FUNCTION in SECS - N seconds from now.  Using
+SECS <= N is not recommended if this function is invoked from an idle
+timer, because FUNCTION will then be called immediately.
 
 If REPEAT is non-nil, do the action each time Emacs has been idle for
 exactly SECS seconds (that is, only once for each time Emacs becomes idle).
@@ -427,10 +427,6 @@ This function returns a timer object which you can use in `cancel-timer'."
     (timer-activate-when-idle timer t)
     timer))
 \f
-(defun with-timeout-handler (tag)
-  "This is the timer function used for the timer made by `with-timeout'."
-  (throw tag 'timeout))
-
 (defvar with-timeout-timers nil
   "List of all timers used by currently pending `with-timeout' calls.")
 
@@ -442,24 +438,27 @@ event (such as keyboard input, input from subprocesses, or a certain time);
 if the program loops without waiting in any way, the timeout will not
 be detected.
 \n(fn (SECONDS TIMEOUT-FORMS...) BODY)"
-  (declare (indent 1))
+  (declare (indent 1) (debug ((form body) body)))
   (let ((seconds (car list))
-       (timeout-forms (cdr list)))
-    `(let ((with-timeout-tag (cons nil nil))
-          with-timeout-value with-timeout-timer
-          (with-timeout-timers with-timeout-timers))
-       (if (catch with-timeout-tag
-            (progn
-              (setq with-timeout-timer
-                    (run-with-timer ,seconds nil
-                                     'with-timeout-handler
-                                     with-timeout-tag))
-              (push with-timeout-timer with-timeout-timers)
-              (setq with-timeout-value (progn . ,body))
-              nil))
-          (progn . ,timeout-forms)
-        (cancel-timer with-timeout-timer)
-        with-timeout-value))))
+       (timeout-forms (cdr list))
+        (timeout (make-symbol "timeout")))
+    `(let ((-with-timeout-value-
+            (catch ',timeout
+              (let* ((-with-timeout-timer-
+                      (run-with-timer ,seconds nil
+                                      (lambda () (throw ',timeout ',timeout))))
+                     (with-timeout-timers
+                         (cons -with-timeout-timer- with-timeout-timers)))
+                (unwind-protect
+                    (progn ,@body)
+                  (cancel-timer -with-timeout-timer-))))))
+       ;; It is tempting to avoid the `if' altogether and instead run
+       ;; timeout-forms in the timer, just before throwing `timeout'.
+       ;; But that would mean that timeout-forms are run in the deeper
+       ;; dynamic context of the timer, with inhibit-quit set etc...
+       (if (eq -with-timeout-value- ',timeout)
+           (progn ,@timeout-forms)
+         -with-timeout-value-))))
 
 (defun with-timeout-suspend ()
   "Stop the clock for `with-timeout'.  Used by debuggers.