Skip to main content
Test case async-test-5 for async-send.
Source Link
Tobias
  • 33.7k
  • 1
  • 38
  • 78

A note on interprocess communication with the async package:

In spite of the fact that async.el defines a function async-send that is not used in that file I assume that the package is not really intendedfit for interprocess communication.

The reason forThere exists a test case async-test-5 for async-send.

I modified that assumptiontest case a bit to make debugging easier:

  • Only async-start and async-start-process are mentioned in the doc string of the package
  • async-ready returns t for the process states exit and signal of the child process -- both leading to the termination of the child process -- and async-wait just waits for async-ready to become true
  • async-get uses async-ready
  • Emacs is invoked with command line option --batch by async-start. So Emacs exists when the PROGRAM has finished.
(defun async-test-5 ()
  (interactive)
  (message "Starting async-test-5...")
  (let ((proc
         (async-start
          ;; What to do in the child process
          (lambda ()
        (with-temp-buffer
          (insert (format "Value of `async-callback': %S\n" async-callback))
          (async-send :hello "world")
          ;; wait for messages
          (while (let* ((msg (async-receive))
                (str (plist-get msg :goodbye)))
               (insert (format "Child got message: %s\n" str))
               (sleep-for 1)
               (null (string-equal str "goodbye"))))
          (buffer-string)))

          ;; What to do when it finishes
          (lambda (result)
            (if (async-message-p result)
                (message "Got hello from child process: %s"
                         (plist-get result :hello))
              (message "Async process done, result: %s"
                       result))))))
    (setq async-message-from-child nil)
    (async-send proc :goodbye "everyone")
    (sleep-for 1)
    (async-send proc :goodbye "just for you")
    (sleep-for 1)
    (async-send proc :goodbye "goodbye"))
  (message "Starting async-test-5...done"))

Running async-test-5 interactively outputs following message:

Async process done, result: Value of `async-callback': nil
Child got message: everyone
Child got message: just for you
Child got message: goodbye

As one sees one can send messages from the mother process to the child but sending messages from the child to the mother process (the (async-send :hello "world") thing) fails.

async-send uses the value of async-callback in the child and that variable is never set in the child. The output string Value of 'async-callback': nil proves that.

For completeness the code of async-send in version 1.9 of async.el:

(defun async-send (&rest args)
  "Send the given messages to the asychronous Emacs PROCESS."
  (let ((args (append args '(:async-message t))))
    (if async-in-child-emacs
        (if async-callback
            (funcall async-callback args))
      (async--transmit-sexp (car args) (list 'quote (cdr args))))))

In spite of the fact that async.el defines a function async-send that is not used in that file I assume that the package is not really intended for interprocess communication.

The reason for that assumption:

  • Only async-start and async-start-process are mentioned in the doc string of the package
  • async-ready returns t for the process states exit and signal of the child process -- both leading to the termination of the child process -- and async-wait just waits for async-ready to become true
  • async-get uses async-ready
  • Emacs is invoked with command line option --batch by async-start. So Emacs exists when the PROGRAM has finished.

A note on interprocess communication with the async package:

In spite of the fact that async.el defines a function async-send I assume that the package is not really fit for interprocess communication.

There exists a test case async-test-5 for async-send.

I modified that test case a bit to make debugging easier:

(defun async-test-5 ()
  (interactive)
  (message "Starting async-test-5...")
  (let ((proc
         (async-start
          ;; What to do in the child process
          (lambda ()
        (with-temp-buffer
          (insert (format "Value of `async-callback': %S\n" async-callback))
          (async-send :hello "world")
          ;; wait for messages
          (while (let* ((msg (async-receive))
                (str (plist-get msg :goodbye)))
               (insert (format "Child got message: %s\n" str))
               (sleep-for 1)
               (null (string-equal str "goodbye"))))
          (buffer-string)))

          ;; What to do when it finishes
          (lambda (result)
            (if (async-message-p result)
                (message "Got hello from child process: %s"
                         (plist-get result :hello))
              (message "Async process done, result: %s"
                       result))))))
    (setq async-message-from-child nil)
    (async-send proc :goodbye "everyone")
    (sleep-for 1)
    (async-send proc :goodbye "just for you")
    (sleep-for 1)
    (async-send proc :goodbye "goodbye"))
  (message "Starting async-test-5...done"))

Running async-test-5 interactively outputs following message:

Async process done, result: Value of `async-callback': nil
Child got message: everyone
Child got message: just for you
Child got message: goodbye

As one sees one can send messages from the mother process to the child but sending messages from the child to the mother process (the (async-send :hello "world") thing) fails.

async-send uses the value of async-callback in the child and that variable is never set in the child. The output string Value of 'async-callback': nil proves that.

For completeness the code of async-send in version 1.9 of async.el:

(defun async-send (&rest args)
  "Send the given messages to the asychronous Emacs PROCESS."
  (let ((args (append args '(:async-message t))))
    (if async-in-child-emacs
        (if async-callback
            (funcall async-callback args))
      (async--transmit-sexp (car args) (list 'quote (cdr args))))))
added 1845 characters in body
Source Link
Tobias
  • 33.7k
  • 1
  • 38
  • 78

Note that (format "This is a %s" var) is run, i.e., the string "This is a value-of-variable" is constructed) before the lambda is used. Therefore, this is not some kind of interprocess-communication where thread safety might be an issue.

Note also that this is quite the intended way to inject variable values into async-start since the doc string of async-inject-variables gives an example that works in that way:

(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)

In spite of the fact that async.el defines a function async-send that is not used in that file I assume that the package is not really intended for interprocess communication.

The reason for that assumption:

  • Only async-start and async-start-process are mentioned in the doc string of the package
  • async-ready returns t for the process states exit and signal of the child process -- both leading to the termination of the child process -- and async-wait just waits for async-ready to become true
  • async-get uses async-ready
  • Emacs is invoked with command line option --batch by async-start. So Emacs exists when the PROGRAM has finished.

Note that (format "This is a %s" var) is run, i.e., the string "This is a value-of-variable" is constructed) before the lambda is used. Therefore, this is not some kind of interprocess-communication where thread safety might be an issue.

Note also that this is quite the intended way to inject variable values into async-start since the doc string of async-inject-variables gives an example that works in that way:

(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)

In spite of the fact that async.el defines a function async-send that is not used in that file I assume that the package is not really intended for interprocess communication.

The reason for that assumption:

  • Only async-start and async-start-process are mentioned in the doc string of the package
  • async-ready returns t for the process states exit and signal of the child process -- both leading to the termination of the child process -- and async-wait just waits for async-ready to become true
  • async-get uses async-ready
  • Emacs is invoked with command line option --batch by async-start. So Emacs exists when the PROGRAM has finished.
added 117 characters in body
Source Link
Tobias
  • 33.7k
  • 1
  • 38
  • 78

The following lisp code demonstrates how the value of a variable var can be injected into the lambda used as START-FUNC in (async-start START-FUNC &optional FINISH-FUNC). I changed the lambda START-FUNC with the help of a back-quote expression such that it returns a string modified with the value of the variable. Since the return value of START-FUNC is printed by FINISH-FUNC it is easier to check that the injection is successful.

(let ((var "value-of-variable"))
  (async-start
   ;; What to do in the child process
   `(lambda ()
      (sleep-for 3)
      ,(format "This is a %s" var))

       ;; What to do when it finishes
       (lambda (result)
         (message "Async process done, result should be 222: %s"
                  result))))

The following lisp code demonstrates how the value of a variable var can be injected into the lambda used as START-FUNC in (async-start START-FUNC &optional FINISH-FUNC). I changed the lambda START-FUNC such that it returns a string modified with the value of the variable. Since the return value of START-FUNC is printed by FINISH-FUNC it is easier to check that the injection is successful.

(let ((var "value-of-variable"))
  (async-start
   ;; What to do in the child process
   `(lambda ()
      (sleep-for 3)
      ,(format "This is a %s" var))

       ;; What to do when it finishes
       (lambda (result)
         (message "Async process done, result should be 222: %s"
                  result))))

The following lisp code demonstrates how the value of a variable var can be injected into the lambda used as START-FUNC in (async-start START-FUNC &optional FINISH-FUNC). I changed the lambda START-FUNC with the help of a back-quote expression such that it returns a string modified with the value of the variable. Since the return value of START-FUNC is printed by FINISH-FUNC it is easier to check that the injection is successful.

(let ((var "value-of-variable"))
  (async-start
   ;; What to do in the child process
   `(lambda ()
      (sleep-for 3)
      ,(format "This is a %s" var))

       ;; What to do when it finishes
       (lambda (result)
         (message "Async process done, result should be 222: %s"
                  result))))
Source Link
Tobias
  • 33.7k
  • 1
  • 38
  • 78
Loading