4

I want to sleep until a predicate is true, without blocking the rest of Emacs from executing.

For example:

(defun clock-striking-one-p ()
  (eq clock 1))

(defun hickory-dickory-dock ()
  (run-up-clock)
  (sleep-until clock-striking-one-p)
  (run-down))
Glorfindel
  • 234
  • 1
  • 5
  • 13
Matthew Piziak
  • 5,958
  • 3
  • 29
  • 77
  • 1
    (General comment here) Answers made for a developement version risk becoming obsolete before this release even goes live. What you are trying to do should be achievable with emacs 24.4 at least. – T. Verron May 27 '15 at 06:49
  • 2
    (Specific for the question) I believe that the problem will be the precision of the "predicate is true" instant : one way or the other, the emacs process will have to check periodically for the predicate. On one extreme, it doesn't wait between checks. Then asynchronous or not, it *will* block emacs. So, is checking for the predicate, say, every second acceptable? In most cases it doesn't matter, but you take an example with a clock event, that's typically a case where the lost precision could be unacceptable. – T. Verron May 27 '15 at 06:50
  • Thank you for the advice regarding development versions. I've removed that part of the question. – Matthew Piziak May 27 '15 at 13:13
  • For my purposes, it is sufficient to check every tenth of a second or so, and so I could create a loop with `sleep-for`. However, I thought that saying so in the question proper could overspecify the question and eliminate interesting and useful answers. What do you think? – Matthew Piziak May 27 '15 at 13:14
  • 1
    I don't think it'd overspecify the question, and I don't think it is necessary to mention it either. It is a reasonable assumption when playing with asynchronous jobs. The opposite would have been worth adding to the question, though. – T. Verron May 27 '15 at 13:20
  • Useful reading here: http://www.emacswiki.org/emacs/ConcurrentEmacs – Joe Corneli May 29 '15 at 11:55
  • 1
    In Emacs, we rarely use such timer-based polling. Instead, we usually place `run-down` (or `(lambda () (when (clock-striking-one-p) (run-down)))`) on some hook somewhere related to the change of the predicate. So, the best answer will depend on the actual predicate. – Stefan Sep 11 '18 at 12:03

1 Answers1

3

I'd set up a periodic timer to check for the condition and cancel the timer when the condition becomes true. Since it requires a little bit of higher-order programming, it's somewhat easier with lexical binding (no need for backquote surgery).

;;-*- lexical-binding: t -*-

(defvar piziak-timer nil
  "The currently active Piziak-style timer")

(defvar piziak-interval 0.1
  "Interval in seconds for Piziak-style timers")

(defun piziak-cancel ()
  "Cancel the current Piziak-style timer"
  (cancel-timer piziak-timer)
  (setq piziak-timer nil))

(defun piziak-do-when (predicate action)
  "Execute action when predicate becomes true.
This sets up a Piziak-style timer."
  (when (not (null piziak-timer))
    (error "I am a Bear of Very Little Brain"))
  (let ((timer (timer-create)))
    (timer-set-time timer (current-time) piziak-interval)
    (timer-set-function
     timer
     ;; This depends on lexical binding
     #'(lambda () (when (funcall predicate)
                    (piziak-cancel)
                    (funcall action))))
    (timer-activate timer)
    (setq piziak-timer timer)))
jch
  • 5,680
  • 22
  • 39
  • Curious here: why do you use `timer-create` and stuff instead of the more customary `run-with-timer` (which I think would be simpler and just as efficient here)? – Stefan Sep 11 '18 at 12:00