[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master d771dff 015/187: Added `async-start-process'
From: |
Michael Albinus |
Subject: |
[elpa] master d771dff 015/187: Added `async-start-process' |
Date: |
Wed, 30 Dec 2015 11:49:25 +0000 |
branch: master
commit d771dff727e5bfff833376b8592c043ad9937046
Author: John Wiegley <address@hidden>
Commit: John Wiegley <address@hidden>
Added `async-start-process'
---
README.md | 74 +++++++++++++++++--
async.el | 207 +++++++++++++++++++++++++++++++++--------------------
smtpmail-async.el | 2 +-
3 files changed, 197 insertions(+), 86 deletions(-)
diff --git a/README.md b/README.md
index fb3e191..39b14b9 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,14 @@
# emacs-async
-async.el is an exceedingly simple module for doing asynchronous processing in
-Emacs, by spawning a child Emacs interpreter to execute a lambda function, and
-calling back when the job is done.
+async.el is a module for doing asynchronous processing in Emacs. The
+interface is intended to be very easy to use:
-It uses a very simple syntax:
+## async-start
+
+ async-start START-FUNC FINISH-FUNC
+
+Execute START-FUNC (often a lambda) in a subordinate Emacs process. When
+done, the return value is passed to FINISH-FUNC. Example:
(async-start
;; What to do in the child process
@@ -16,9 +20,9 @@ It uses a very simple syntax:
;; What to do when it finishes
(lambda (result)
(message "Async process done, result should be 222: %s" result)))
-
-If you omit the callback function, `async-start` returns a process object that
-you can call `async-get` on when you're ready to wait for the result value:
+
+If FINISH-FUNC is nil or missing, a future is returned that can be inspected
+using `async-get', blocking until the value is ready. Example:
(let ((proc (async-start
;; What to do in the child process
@@ -33,7 +37,7 @@ you can call `async-get` on when you're ready to wait for the
result value:
(async-get proc)))
If you don't want to use a callback, and you don't care about any return value
-form the child process, pass the `ignore` symbol as the second argument (if
+form the child process, pass the `ignore' symbol as the second argument (if
you don't, and never call `async-get', it will leave *emacs* process buffers
hanging around):
@@ -41,3 +45,57 @@ hanging around):
(lambda ()
(delete-file "a remote file on a slow link" nil))
'ignore)
+
+Note: Even when FINISH-FUNC is present, a future is still returned except that
+it yields no value (since the value is passed to FINISH-FUNC). Call
+`async-get' on such a future always returns nil. It can still be useful,
+however, as an argument to `async-ready' or `async-wait'.
+
+## async-start-process
+
+ async-start-process NAME PROGRAM FINISH-FUNC &rest PROGRAM-ARGS
+
+Start the executable PROGRAM asynchronously. See `async-start'. PROGRAM is
+passed PROGRAM-ARGS, calling FINISH-FUNC with the process object when done.
+If FINISH-FUNC is nil, the future object will return the process object when
+the program is finished.
+
+## async-get
+
+ async-get FUTURE
+
+Get the value from an asynchronously function when it is ready. FUTURE is
+returned by `async-start' or `async-start-process' when its FINISH-FUNC is
+nil.
+
+## async-ready
+
+ async-ready FUTURE
+
+Query a FUTURE to see if the ready is ready -- i.e., if no blocking
+would result from a call to `async-get' on that FUTURE.
+
+## async-wait
+
+ async-wait FUTURE
+
+Wait for FUTURE to become ready.
+
+## async-inject-variables
+
+ async-inject-variables INCLUDE-REGEXP &optional PREDICATE EXCLUDE-REGEXP
+
+Return a `setq' form that replicates part of the calling environment. It sets
+the value for every variable matching INCLUDE-REGEXP and also PREDICATE. It
+will not perform injection for any variable matching EXCLUDE-REGEXP (if
+present). It is intended to be used as follows:
+
+ (async-start
+ `(lambda ()
+ (require 'smtpmail)
+ (with-temp-buffer
+ (insert ,(buffer-substring-no-properties (point-min) (point-max)))
+ ;; Pass in the variable environment for smtpmail
+ ,(async-inject-variables "\\`\\(smtpmail\\|\\(user-\\)?mail\\)-")
+ (smtpmail-send-it)))
+ 'ignore)
diff --git a/async.el b/async.el
index 6b3831c..070803b 100644
--- a/async.el
+++ b/async.el
@@ -4,9 +4,9 @@
;; Author: John Wiegley <address@hidden>
;; Created: 18 Jun 2012
-;; Version: 1.0
+;; Version: 1.1
;; Keywords: async
-;; X-URL: https://github.com/jwiegley/async
+;; X-URL: https://github.com/jwiegley/emacs-async
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
@@ -25,42 +25,8 @@
;;; Commentary:
-;; Adds the ability to process Lisp concurrently, with a very simple syntax:
-;;
-;; (async-start
-;; ;; What to do in the child process
-;; (lambda ()
-;; (message "This is a test")
-;; (sleep-for 3)
-;; 222)
-;;
-;; ;; What to do when it finishes
-;; (lambda (result)
-;; (message "Async process done, result should be 222: %s" result)))
-;;
-;; If you omit the callback function, `async-start' will return a process
-;; object that you can call `async-get' on when you're ready to wait for the
-;; result value:
-;;
-;; (let ((proc (async-start
-;; ;; What to do in the child process
-;; (lambda ()
-;; (message "This is a test")
-;; (sleep-for 3)
-;; 222))))
-;; (message "I'm going to do some work here")
-;; ;; ....
-;; (message "Async process done, result should be 222: %s"
-;; (async-get proc)))
-;;
-;; If you don't want to use a callback, and you don't care about any return
-;; value form the child proces, pass the `ignore' symbol as the second
-;; argument:
-;;
-;; (async-start
-;; (lambda ()
-;; (delete-file "a remote file on a slow link" nil))
-;; 'ignore)
+;; Adds the ability to call asynchronous functions and process with ease. See
+;; the documentation for `async-start' and `async-start-process'.
;;; Code:
@@ -69,12 +35,27 @@
:group 'emacs)
(defvar async-callback nil)
+(defvar async-callback-for-process nil)
(defvar async-callback-value nil)
(defvar async-callback-value-set nil)
-(defmacro async-inject-environment
+(defmacro async-inject-variables
(include-regexp &optional predicate exclude-regexp)
- "Inject a part of the parent environment into an async function."
+ "Return a `setq' form that replicates part of the calling environment.
+It sets the value for every variable matching INCLUDE-REGEXP and
+also PREDICATE. It will not perform injection for any variable
+matching EXCLUDE-REGEXP (if present). It is intended to be used
+as follows:
+
+ (async-start
+ `(lambda ()
+ (require 'smtpmail)
+ (with-temp-buffer
+ (insert ,(buffer-substring-no-properties (point-min) (point-max)))
+ ;; Pass in the variable environment for smtpmail
+ ,(async-inject-variables \"\\`\\(smtpmail\\|\\(user-\\)?mail\\)-\")
+ (smtpmail-send-it)))
+ 'ignore)"
`'(setq
,@(let (bindings)
(mapatoms
@@ -96,12 +77,20 @@
bindings (cons sym bindings)))))))
bindings)))
+(defalias 'async-inject-environment 'async-inject-variables)
+
(defun async-when-done (proc &optional change)
"Process sentinal used to retrieve the value from the child process."
(when (eq 'exit (process-status proc))
(with-current-buffer (process-buffer proc)
(if (= 0 (process-exit-status proc))
- (progn
+ (if async-callback-for-process
+ (if async-callback
+ (prog1
+ (funcall async-callback proc)
+ (kill-buffer (current-buffer)))
+ (set (make-local-variable 'async-callback-value) proc)
+ (set (make-local-variable 'async-callback-value-set) t))
(goto-char (point-max))
(backward-sexp)
(let ((result (read (current-buffer))))
@@ -126,54 +115,107 @@
"Called from the child Emacs process' command-line."
(condition-case err
(prin1 (funcall (eval (read nil))))
- (signal
- (prin1 `(async-signal . ,err)))
(error
(prin1 `(async-signal . ,err)))))
-(defun async-ready (proc)
- "Wait until PROC has successfully completed."
- (and (eq 'exit (process-status proc))
+(defun async-ready (future)
+ "Query a FUTURE to see if the ready is ready -- i.e., if no blocking
+would result from a call to `async-get' on that FUTURE."
+ (and (eq 'exit (process-status future))
async-callback-value-set))
-(defun async-wait (proc)
- "Wait until PROC has successfully completed."
- (while (not (async-ready proc))
+(defun async-wait (future)
+ "Wait for FUTURE to become ready."
+ (while (not (async-ready future))
(sit-for 0 50)))
-(defun async-get (proc)
- "Wait until PROC has successfully completed."
- (async-wait proc)
- (with-current-buffer (process-buffer proc)
+(defun async-get (future)
+ "Get the value from an asynchronously function when it is ready.
+FUTURE is returned by `async-start' or `async-start-process' when
+its FINISH-FUNC is nil."
+ (async-wait future)
+ (with-current-buffer (process-buffer future)
(prog1
async-callback-value
(kill-buffer (current-buffer)))))
+;;;###autoload
+(defmacro async-start-process (name program finish-func &rest program-args)
+ "Start the executable PROGRAM asynchronously. See `async-start'.
+PROGRAM is passed PROGRAM-ARGS, calling FINISH-FUNC with the
+process object when done. If FINISH-FUNC is nil, the future
+object will return the process object when the program is
+finished."
+(let ((bufvar (make-symbol "buf"))
+ (procvar (make-symbol "proc")))
+ `(let* ((,bufvar (generate-new-buffer ,(concat "*" name "*")))
+ (,procvar
+ ,`(apply #'start-process ,name ,bufvar ,program
+ (quote ,program-args))))
+ (with-current-buffer ,bufvar
+ (set (make-local-variable 'async-callback) ,finish-func)
+ (set-process-sentinel ,procvar #'async-when-done)
+ ,(unless (string= name "emacs")
+ '(set (make-local-variable 'async-callback-for-process) t))
+ ,procvar))))
+
+;;;###autoload
(defmacro async-start (start-func &optional finish-func)
- "Fork execution of `start-func' into its own Emacs process.
-`start-func' must be a `read'-able symbol or lambda form. It
-cannot be a byte-compiled lambda.
-
-`finish-func' is called with the result of `start-func' when that
-process has completed. If it is nil, `async-start' will return a
-process object that you can block on with `async-future-get' in
-order to wait for the result of `start-func'. This would allow
-you to start some expensive background processing at the
-beginning of a command, then wait for the result only when you're
-ready to use it."
- (let ((bufvar (make-symbol "buf"))
- (procvar (make-symbol "proc")))
- (require 'find-func)
- `(let* ((,bufvar (generate-new-buffer "*emacs*"))
- (,procvar
- (start-process "emacs" ,bufvar
- (expand-file-name invocation-name
- invocation-directory)
- "-Q" "-l" (find-library-name "async")
- "-batch" "-f" "async-batch-invoke")))
- (with-current-buffer ,bufvar
- (set (make-local-variable 'async-callback) ,finish-func)
- (set-process-sentinel ,procvar #'async-when-done)
+ "Execute START-FUNC (often a lambda) in a subordinate Emacs process.
+When done, the return value is passed to FINISH-FUNC. Example:
+
+ (async-start
+ ;; What to do in the child process
+ (lambda ()
+ (message \"This is a test\")
+ (sleep-for 3)
+ 222)
+
+ ;; What to do when it finishes
+ (lambda (result)
+ (message \"Async process done, result should be 222: %s\"
+ result)))
+
+If FINISH-FUNC is nil or missing, a future is returned that can
+be inspected using `async-get', blocking until the value is
+ready. Example:
+
+ (let ((proc (async-start
+ ;; What to do in the child process
+ (lambda ()
+ (message \"This is a test\")
+ (sleep-for 3)
+ 222))))
+
+ (message \"I'm going to do some work here\") ;; ....
+
+ (message \"Waiting on async process, result should be 222: %s\"
+ (async-get proc)))
+
+If you don't want to use a callback, and you don't care about any
+return value form the child process, pass the `ignore' symbol as
+the second argument (if you don't, and never call `async-get', it
+will leave *emacs* process buffers hanging around):
+
+ (async-start
+ (lambda ()
+ (delete-file \"a remote file on a slow link\" nil))
+ 'ignore)
+
+Note: Even when FINISH-FUNC is present, a future is still
+returned except that it yields no value (since the value is
+passed to FINISH-FUNC). Call `async-get' on such a future always
+returns nil. It can still be useful, however, as an argument to
+`async-ready' or `async-wait'."
+ (require 'find-func)
+ (let ((procvar (make-symbol "proc")))
+ `(let ((,procvar
+ (async-start-process "emacs" (expand-file-name invocation-name
+
invocation-directory)
+ ,finish-func
+ "-Q" "-l" ,(find-library-name "async")
+ "-batch" "-f" "async-batch-invoke")))
+ (with-current-buffer (process-buffer ,procvar)
(with-temp-buffer
(let ((print-escape-newlines t))
(prin1 (list 'quote ,start-func) (current-buffer)))
@@ -227,6 +269,17 @@ ready to use it."
(message "Async process done, result should be 222: %s" result)))
(message "Starting async-test-1...done"))
+(defun async-test-4 ()
+ (interactive)
+ (message "Starting async-test-4...")
+ (async-start-process "sleep" "sleep"
+ ;; What to do when it finishes
+ (lambda (proc)
+ (message "Sleep done, exit code was %d"
+ (process-exit-status proc)))
+ "3")
+ (message "Starting async-test-4...done"))
+
(provide 'async)
;;; async.el ends here
diff --git a/smtpmail-async.el b/smtpmail-async.el
index 42fb6d7..4750ce6 100644
--- a/smtpmail-async.el
+++ b/smtpmail-async.el
@@ -48,7 +48,7 @@
(with-temp-buffer
(insert ,(buffer-substring-no-properties (point-min) (point-max)))
;; Pass in the variable environment for smtpmail
- ,(async-inject-environment "\\`\\(smtpmail\\|\\(user-\\)?mail\\)-")
+ ,(async-inject-variables "\\`\\(smtpmail\\|\\(user-\\)?mail\\)-")
(smtpmail-send-it)))
'ignore))
- [elpa] master 1ad2902 011/187: Quiet byte-compiler warnings, (continued)
- [elpa] master 1ad2902 011/187: Quiet byte-compiler warnings, Michael Albinus, 2015/12/30
- [elpa] master 742c82e 020/187: Added message passing, but undocumented for now, Michael Albinus, 2015/12/30
- [elpa] master b80f1a5 018/187: Don't use pipes for communication just yet, Michael Albinus, 2015/12/30
- [elpa] master 497e4da 024/187: Fix to dired-async for wdired-mode, Michael Albinus, 2015/12/30
- [elpa] master e08c251 017/187: Added `dired-async-use-native-commands', Michael Albinus, 2015/12/30
- [elpa] master 7ba4f40 022/187: Always base64 encode, Michael Albinus, 2015/12/30
- [elpa] master 1028235 023/187: Don't use lexical-binding in dired-async.el, Michael Albinus, 2015/12/30
- [elpa] master 12ca991 016/187: Made some macros into functions, added debug code, Michael Albinus, 2015/12/30
- [elpa] master 386a876 019/187: Some minor adjustments, Michael Albinus, 2015/12/30
- [elpa] master dfaddaa 025/187: Use pipes instead of variable passing, Michael Albinus, 2015/12/30
- [elpa] master d771dff 015/187: Added `async-start-process',
Michael Albinus <=
- [elpa] master 66610f4 026/187: smtpmail-async: Report status to user when done, Michael Albinus, 2015/12/30
- [elpa] master b7ec203 021/187: Fix for when async.el is byte-compiled, Michael Albinus, 2015/12/30
- [elpa] master 3f870f5 028/187: Show ops in progress `dired-async-in-process-face', Michael Albinus, 2015/12/30
- [elpa] master 1cec376 030/187: Added async-sandbox, Michael Albinus, 2015/12/30
- [elpa] master 24811ee 027/187: Show full backtraces if `async-debug' is non-nil, Michael Albinus, 2015/12/30
- [elpa] master 15f737f 032/187: Propagate non-zero exit codes as errors, Michael Albinus, 2015/12/30
- [elpa] master eda8d32 035/187: * helm-async.el (dired-create-file): Use quote., Michael Albinus, 2015/12/30
- [elpa] master ec1f0e8 034/187: * helm-async.el: let-bind dired-recursive-copies to 'always to avoid hanging on child., Michael Albinus, 2015/12/30
- [elpa] master acb0885 031/187: Several minor fixes, Michael Albinus, 2015/12/30
- [elpa] master 49f8b81 038/187: * helm-async.el: Update copyright, Michael Albinus, 2015/12/30