Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

run-with-idle-timer only runs proc once

0 views
Skip to first unread message

David M. Karr

unread,
Jul 17, 2000, 3:00:00 AM7/17/00
to
I've used "run-with-idle-timer" for quite a while, over several
releases of GNU Emacs. It always seemed to work correctly. I'm now
up to 20.6 on Solaris, and 20.7 on NT. Now, for some reason, the
process specified only runs once, and never runs again. It seems like
it never thinks Emacs is idle after the first run. If I use
"run-with-timer" instead, however, it runs every REPEAT seconds.

After some perusal of the "timer.el" code, it seems like there is some
issue with the "REPEAT" parameter to "run-with-idle-timer". The value
of "REPEAT" seems to be either nil or non-nil, according to the
documentation. However, some code in timer.el seems to think it
represents a number of seconds. Nevertheless, when I change my call
to have the "REPEAT" be a number (5, for instance), the timer still
only fires once, never firing again.

--
===============================================================================
David M. Karr ; dk...@tcsi.com ; w:(425)487-8312 ; TCSI & Best Consulting
Software Engineer ; Unix/Java/C++/X ; BrainBench CJ12P (6/12/2000)

David M. Karr

unread,
Jul 17, 2000, 3:00:00 AM7/17/00
to
>>>>> "David" == David M Karr <dk...@tcsi.com> writes:
David> I've used "run-with-idle-timer" for quite a while, over several
David> releases of GNU Emacs. It always seemed to work correctly. I'm now
David> up to 20.6 on Solaris, and 20.7 on NT. Now, for some reason, the
David> process specified only runs once, and never runs again. It seems like
David> it never thinks Emacs is idle after the first run. If I use
David> "run-with-timer" instead, however, it runs every REPEAT seconds.

David> After some perusal of the "timer.el" code, it seems like there is some
David> issue with the "REPEAT" parameter to "run-with-idle-timer". The value
David> of "REPEAT" seems to be either nil or non-nil, according to the
David> documentation. However, some code in timer.el seems to think it
David> represents a number of seconds. Nevertheless, when I change my call
David> to have the "REPEAT" be a number (5, for instance), the timer still
David> only fires once, never firing again.

After a little more investigation, I see that an "idle timer" will
only execute if Emacs has been idle for SECS seconds, such that
immediately before that, it was NOT idle. So for instance, if I set a
5 second idle timer, to execute every 5 seconds, it will only execute
once if I just leave it alone. However, if I then make Emacs un-idle
momentarily, then let it sit again, it will execute the timer again
after 5 seconds, but then only once, until I "un-idle" Emacs again.

Am I misinterpreting how "run-with-idle-timer" is SUPPOSED to work? I
really need it to work the way it appears to be documented, so it will
continue to execute each SECS seconds after Emacs became idle, not
just once.

I thought I could fool it by cancelling and resetting the timer each
time the function ran, but that still doesn't do it. I still need to
interrupt each "idle period" with some manual action, in order for the
idle timer to fire, but it still only does it once.

Andrew Innes

unread,
Jul 18, 2000, 3:00:00 AM7/18/00
to David M. Karr

I'm not really sure what the intended REPEAT semantics are, although I
can see the reasoning behind the implemented semantics. (Things like
lazy-lock use repeating idle timers to check whether they have any work
to do, and only need to check once after a suitable amount of idleness.)

I've implemented a POP mail check extension for Gnus, which needs to
periodically poll the POP server. Following the example of epop3mail
(an enhancement to pop3.el which provides similar "biff" functionality),
I arrange for the check to be postponed until Emacs has been idle for at
a few seconds to avoid interrupting work. This is done by having a
normal repeating timer which creates an idle timer if one isn't already
queued. (See code below, from mail-source.el in Gnus.)

>I thought I could fool it by cancelling and resetting the timer each
>time the function ran, but that still doesn't do it. I still need to
>interrupt each "idle period" with some manual action, in order for the
>idle timer to fire, but it still only does it once.

I ran into the same (or a closely related) problem with my mail-check
extension. The problem stems from the fact that idle timers do not get
"primed" until Emacs makes a transition from non-idle to idle. That
means that if an idle timer is created when Emacs is already idle (as
happens frequently with my mail-check code), the idle timer isn't
actually activated until the user does something.

To get round this problem, on Emacs at least, I poke the timer object (a
simple vector) to force it into the primed state immediately, so it will
run immediately if Emacs has already been idle for the required time, or
when it reaches that point. (This won't work on XEmacs where timer
objects are opaque.)

It seems to me that it is a bug or misfeature that newly
created/activated idle timers aren't "primed" initially. I think that
either idle timers should be primed automatically (my preference, since
I can't think of any reason not to do so), or an officially-sanctioned
method for doing so should be provided.

AndrewI

-----------------------------------------------------------------
(defvar mail-source-report-new-mail nil)
(defvar mail-source-report-new-mail-timer nil)
(defvar mail-source-report-new-mail-idle-timer nil)

(eval-when-compile (require 'timer))

(defun mail-source-start-idle-timer ()
;; Start our idle timer if necessary, so we delay the check until the
;; user isn't typing.
(unless mail-source-report-new-mail-idle-timer
(setq mail-source-report-new-mail-idle-timer
(run-with-idle-timer
mail-source-idle-time-delay
nil
(lambda ()
(mail-source-check-pop mail-source-primary-source)
(setq mail-source-report-new-mail-idle-timer nil))))
;; Since idle timers created when Emacs is already in the idle
;; state don't get activated until Emacs _next_ becomes idle, we
;; need to force our timer to be considered active now. We do
;; this by being naughty and poking the timer internals directly
;; (element 0 of the vector is nil if the timer is active).
(aset mail-source-report-new-mail-idle-timer 0 nil)))

(defun mail-source-report-new-mail (arg)
"Toggle whether to report when new mail is available.
This only works when `display-time' is enabled."
(interactive "P")
(if (not mail-source-primary-source)
(error "Need to set `mail-source-primary-source' to check for new mail."))
(let ((on (if (null arg)
(not mail-source-report-new-mail)
(> (prefix-numeric-value arg) 0))))
(setq mail-source-report-new-mail on)
(and mail-source-report-new-mail-timer
(cancel-timer mail-source-report-new-mail-timer))
(and mail-source-report-new-mail-idle-timer
(cancel-timer mail-source-report-new-mail-idle-timer))
(setq mail-source-report-new-mail-timer nil)
(setq mail-source-report-new-mail-idle-timer nil)
(if on
(progn
(require 'time)
(setq display-time-mail-function #'mail-source-new-mail-p)
;; Set up the main timer.
(setq mail-source-report-new-mail-timer
(run-at-time t (* 60 mail-source-report-new-mail-interval)
#'mail-source-start-idle-timer))
;; When you get new mail, clear "Mail" from the mode line.
(add-hook 'nnmail-post-get-new-mail-hook
'display-time-event-handler)
(message "Mail check enabled"))
(setq display-time-mail-function nil)
(remove-hook 'nnmail-post-get-new-mail-hook
'display-time-event-handler)
(message "Mail check disabled"))))
-----------------------------------------------------------------

0 new messages