Using Emacs Threads To Execute Commands Asynchronously

1 Executive Summary

Emacs 26 has threads for executing functions asynchronously. Emacs commands that call an external process and wait for that process to finish make a good candidate for asynchronous execution — e.g., smtpmail-send-it for sending mail. The arrival of threads provides an interesting option for running such commands asynchronously.

2 First Attempt — Custom Async gnus Command

I initially wrote a custom command for launching gnus asynchronously — it was a one-line function that ran the following:

(make-thread #'gnus)

The above worked well — except when command gnus needed user input — so I just had to be thoughtful about when I called it. But a few weeks later, I wanted the equivalent for function smtpmail-send-it for sending mail. I almost wrote myself one more command before stepping back to create a more generic solution.

3 One Command To Thread Them All

I have now defined command emacspeak-wizards-execute-asynchronously bound to C-' a. Note that this command, though part of module emacspeak-wizards, has no emacspeak dependencies.

(defun emacspeak-wizards-execute-asynchronously (key)
  "Read key-sequence, then execute its command on a new thread."
  (interactive (list (read-key-sequence "Key Sequence: ")))
  (let ((l  (local-key-binding key))
        (g (global-key-binding key))
         (when-let (map(get-text-property (point) 'keymap))
           (lookup-key map key))))
        ((do-it (command)
                (make-thread command)
                (message "Running %s on a new thread." command)))
       ( (commandp k) (do-it k))
       ( (commandp l) (do-it l))
       ((commandp g) (do-it g))
       (t (error "%s is not bound to a command." key))))))

(global-set-key (kbd "C-' a") 'emacspeak-wizards-execute-asynchronously)

With this command bound to C-' a, I can now get rid of my custom gnus-async command and its associated key-binding. I already have command gnus bound to C-; g, so I can just press C-' a C-; g to fetch news/mail asynchronously.

Similarly, when sending mail using smtpmail I can press C-' a C-c C-c in the *mail* buffer to send mail without Emacs blocking.

4 Final Caveats

Like other asynchronous solutions (see package async for instance) one needs to make sure that the command being executed asynchronously will not require user input. In the case of package async, the asynchronous Emacs will block waiting for input; in the case of make-thread, Emacs enters a blocking loop with the minibuffer continuously displaying

No catch for ...

The only way to come out is to kill Emacs — so make sure to use command emacspeak-wizards-execute-asynchronously only when you're sure that the command being run asynchronously will not require user input.

Date: 2018-07-03 Tue 00:00

Author: T.V Raman

Created: 2018-07-04 Wed 08:32