Lisp HUG Maillist Archive

[Q] Unscheduling a timer in its expiry function

Hello All,

Can an expiry function for a timer unschedule the timer? This is not
clear from the documentation, but in the test code below, it doesn't
work.

----------------
(in-package "CL-USER")

(defvar *test-timer* nil)
(defvar *test-count* 0)
(defun test-callback (stream)
  (princ "ding!" stream)
  (cond ((< *test-count* 5)
         (incf *test-count*))
        ((>= *test-count* 5)
         (princ " dong! " stream)
         (mp:unschedule-timer *test-timer*)))
  (terpri stream))
(progn
  (setq *test-timer* (mp:make-timer #'test-callback *standard-output*))
  (setq *test-count* 0)
  (mp:schedule-timer *test-timer* 0 1))

;; Use the following to unschedule the timer
;;
(mp:unschedule-timer *test-timer*)
----------------

Eval'ing the PROGN, ought to kick off the timer, and after five calls
to TEST-CALLBACK, I was expecting the timer to be unscheduled.

I looked in some of the CAPI examples (notably pong.lisp) and I found
some code as follows:

(defun play-game (self)
	:
        :
    (setf timer (mp:make-timer 'make-a-pong-move self))
    (mp:schedule-timer timer 0 delay))

MAKE-A-PONG-MOVE is the timer expiry function.

(defun make-a-pong-move (self)
  (unless (capi-internals:representation self)
    (with-slots (timer) self
      (when timer 
        (mp:unschedule-timer timer)))
    (return-from make-a-pong-move nil))
	:
	:
    )

But, I'm not sure if and under what circumstances this code is
actually exercised. There are also other places in the code where, a
call to UNSCHEDULE-TIMER is made but within the scope of a
CAPI:EXECUTE-WITH-INTERFACE, which presumably occurs in the CAPI
thread and will work as expected.

If it's not possible to unschedule a timer from within it's expiry
function, then what is a recommended way to do this without
sacrificing abstraction.

Thanks,

-ram


Re: [Q] Unscheduling a timer in its expiry function

Unable to parse email body. Email id is 339

Re: [Q] Unscheduling a timer in its expiry function

Nick,

Thanks again.  I did indeed miss the significance of the timer code
running in the idle process.

The context for this question is that I'm scheduling a timer for a
1-second interval, so rather than forking a new process upon each call
to the callback function, I've created a mailbox between an "observer"
process and the timer callback as follows:

----------------
(defun seconds-timer-expire-callback ()
  (with-lock (*seconds-timer-lock* :timeout 1)
    (dolist (mbox *seconds-timer-mailboxes*)
      (mailbox-send mbox 'tick))))

(defvar *seconds-timer-timer* (make-timer #'seconds-timer-expire-callback))
----------------

Each observer process (there aren't too many of them) adds its mailbox
to the *seconds-timer-mailboxes* list and listens for the 'tick
messages. I trust this mitigates the potential for disaster in the
idle process.

Regards,

-ram

Nick Levine <ndl@ravenbrook.com> writes:

> I think I sent out warning mail about this recently. 
>
> TIMER CODE RUNS IN THE IDLE PROCESS. IT IS ALWAYS IN YOUR BEST
> INTERESTS TO GET OUT OF THE IDLE PROCESS AS RAPIDLY AS POSSIBLE.
>
> I have no idea why mp:unschedule-timer doesn't work within the idle
> process. I barely care. 
>
> However, either of the following does work. For the reasons shouted
> above, I strongly recommend the latter.
>
> - nick
>
> -----------------------------------------
>
> (defun test-callback (stream)
>   (princ "ding!" stream)
>   (cond ((< *test-count* 5)
>          (incf *test-count*))
>         ((>= *test-count* 5)
>          (princ " dong! " stream)
>          (mp:process-run-function "Unscheduling the timer" () 
>                                   'mp:unschedule-timer *test-timer*)))
>   (terpri stream))
>
> (setf *test-timer* (mp:make-timer 'test-callback *standard-output*))
> (setf *test-count* 0)
> (mp:schedule-timer *test-timer* 0 1)
>
> -----------------------------------------
>
> (defun test-callback (stream)
>   (princ "ding!" stream)
>   (cond ((< *test-count* 5)
>          (incf *test-count*))
>         ((>= *test-count* 5)
>          (princ " dong! " stream)
>          (mp:unschedule-timer *test-timer*)))
>   (terpri stream))
>
> (setf *test-timer* (mp:make-timer 'mp:process-run-function "Running the timer" () 
>                                   'test-callback *standard-output*))
> (setf *test-count* 0)
> (mp:schedule-timer *test-timer* 0 1)


Updated at: 2020-12-10 09:02 UTC