branch: externals/futur
commit 3e8e236123b99370fce4a75951a72f16097837f3
Author: Stefan Monnier <[email protected]>
Commit: Stefan Monnier <[email protected]>

    (futur-blocking-wait-to-get-result): Align with `futur-bind`
    
    * futur.el (futur--handle-error): New function, extracted from `futur-bind`.
    (futur-bind): Use it.
    (futur-blocking-wait-to-get-result): Use it as well.
    Abort the future if the user `quit`s.
    (futur-process-call): Try and avoid a weird error.
---
 futur.el | 74 +++++++++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 48 insertions(+), 26 deletions(-)

diff --git a/futur.el b/futur.el
index 50d30fef1b..12abf00517 100644
--- a/futur.el
+++ b/futur.el
@@ -330,9 +330,6 @@ A futur has 3 possible states:
     ((futur--waiting _ clients)
      (setf (futur--clients futur) (if err 'error t))
      (setf (futur--value futur) (or err val))
-     ;; FIXME: Should we just always abort the blocker instead of
-     ;; doing it only from `futur-abort'?
-     ;;(futur-blocker-abort blocker)
      ;; CLIENTS is usually in reverse order since we always `push' to them.
      (dolist (client (nreverse clients))
        ;; Don't run the clients directly from here, so we don't nest,
@@ -410,6 +407,21 @@ If FUTUR already completed, FUN is called immediately."
   "Make sure VAL is a `futur'.  If not, make it a trivial one that returns 
VAL."
   (if (futur--p val) val (futur--done val)))
 
+(defun futur--handle-error (error-fun err runner default)
+ "Select the appropriate element of ERROR-FUN for ERR.
+Passes it to RUNNER (with ERR as second argument)
+if found, and if no handler applies, calls DEFAULT with ERR
+as argument."
+  (if (functionp error-fun)
+      (funcall runner error-fun err)
+    (while (and error-fun
+                (not (futur--error-member-p
+                      err (caar error-fun))))
+      (setq error-fun (cdr error-fun)))
+    (if error-fun
+        (funcall runner (cdar error-fun) err)
+      (funcall default err))))
+
 (defun futur-bind (futur fun &optional error-fun)
   "Build a new future by composition.
 That future calls FUN with the return value of FUTUR and returns
@@ -431,17 +443,12 @@ as-is to FUN."
        futur (lambda (err val)
                (cond
                 ((null err) (futur--run-continuation new fun (list val)))
-                (error-fun
-                 (if (functionp error-fun)
-                     (futur--run-continuation new error-fun (list err))
-                   (while (and error-fun
-                               (not (futur--error-member-p
-                                     err (caar error-fun))))
-                     (setq error-fun (cdr error-fun)))
-                   (if error-fun
-                       (futur--run-continuation new (cdar error-fun) (list 
err))
-                     (futur-deliver-failure new err))))
-                (t (futur-deliver-failure new err))))))
+                (t
+                 (futur--handle-error
+                  error-fun err
+                  (lambda (error-fun err)
+                    (futur--run-continuation new error-fun (list err)))
+                  (lambda (err) (futur-deliver-failure new err))))))))
     new))
 
 (defun futur--run-continuation (futur fun args)
@@ -491,17 +498,22 @@ its result, or (re)signals the error if ERROR-FUN is nil."
     (error "Blocking/waiting within an asynchronous context is not supported"))
   (if (not (futur--p futur))
       futur
-    (if t ;; (null futur--idle-loop-bug80286)
-        (futur-blocker-wait futur)
-      (let* ((mutex (make-mutex "futur-wait"))
-             (condition (make-condition-variable mutex)))
-        (with-mutex mutex
-          (futur--register-callback futur (lambda (_err _val)
-                                            (with-mutex mutex
-                                              (condition-notify condition))))
-          (condition-wait condition))))
+    (when (futur--waiting-p futur)
+      (condition-case err
+          (if t ;; (null futur--idle-loop-bug80286)
+              (futur-blocker-wait futur)
+            (let* ((mutex (make-mutex "futur-wait"))
+                   (condition (make-condition-variable mutex)))
+              (with-mutex mutex
+                (futur--register-callback
+                 futur (lambda (_err _val)
+                         (with-mutex mutex (condition-notify condition))))
+                (condition-wait condition))))
+        ;; FIXME: Use `handler-bind' instead of `futur--resignal'.
+        (quit (futur-abort futur) (futur--resignal err))))
     (pcase-exhaustive futur
-      ((futur--failed err) (funcall (or error-fun #'futur--resignal) err))
+      ((futur--failed err)
+       (futur--handle-error error-fun err #'funcall #'futur--resignal))
       ((futur--done val) val))))
 
 (defmacro futur-let* (bindings &rest body)
@@ -554,7 +566,8 @@ clients are `futur--aux' functions.")
   "Make sure FUN is called, with no arguments, once FUTUR completes.
 Calls it both when FUTUR succeeds and when it fails.
 Unlike what happens with `unwind-protect', there is no guarantee of
-exactly when FUN is called, other than not before FUTUR completes."
+exactly when FUN is called, other than not before FUTUR completes.
+The return value of FUN is ignored."
   ;; FIXME: Not sure if this implementation is making enough efforts to make
   ;; sure not to forget to run FUN.  Maybe we should register FUTUR+FUN
   ;; on some global list somewhere that we can occasionally scan, in case
@@ -828,7 +841,16 @@ The DISPLAY argument is ignored: redisplay always happens."
     (when (stringp destination)
       (write-region "" nil destination nil 'silent))
     (pcase-exhaustive infile
-      ('nil (process-send-eof proc))
+      ;; FIXME: Not sure how this can happen, but I got backtraces like:
+      ;;
+      ;;     Debugger entered--Lisp error: (error "Process diff not running: 
exited abnormally with code 1\n")
+      ;;       process-send-eof(#<process diff>)
+      ;;       futur-process-call("diff" nil t nil "-ad" "/tmp/diff1UHFpgS" 
"/tmp/diff2k4UPTf")
+      ;;
+      ;; So let's double-check that `proc' is still alive before
+      ;; sending EOF, tho I'm not sure it's sufficient.
+      ('nil (when (process-live-p proc)
+              (process-send-eof proc)))
       ((pred stringp) (futur-send-file proc infile)))
     (process-put proc 'futur-destination destination)
     futur))

Reply via email to