remove all the 8run and 8port-alias things
[8sync.git] / eightsync / agenda.scm
index 8905c1b6c46241d4f1aac9dfe4d4a14a2077d5c3..386752695c41968c96a81df2d13e86983dae2220 100644 (file)
@@ -50,7 +50,7 @@
             schedule-segments-split schedule-extract-until!
             add-segments-contents-to-queue!
 
-            %sync 8sync
+            %8sync
 
             <run-request>
             make-run-request run-request?
             port-request-port
             port-request-read port-request-write port-request-except
 
-            run-it wrap run run-at run-delay
+            run-it wrap wrap-apply run run-at run-delay
 
             %port-request %run %run-at %run-delay
-            8port-request 8run 8run-at 8run-delay
             
+            print-error-and-continue
+
             %current-agenda
             start-agenda agenda-run-once))
 
@@ -93,7 +94,7 @@
 (define-immutable-record-type <agenda>
   (make-agenda-intern queue prompt-tag
                       read-port-map write-port-map except-port-map
-                      schedule time)
+                      schedule time catch-handler pre-unwind-handler)
   agenda?
   (queue agenda-queue)
   (prompt-tag agenda-prompt-tag)
   (write-port-map agenda-write-port-map)
   (except-port-map agenda-except-port-map)
   (schedule agenda-schedule)
-  (time agenda-time))
+  (time agenda-time)
+  (catch-handler agenda-catch-handler)
+  (pre-unwind-handler agenda-pre-unwind-handler))
 
 (define (make-async-prompt-tag)
   "Make an async prompt tag for an agenda.
@@ -116,12 +119,15 @@ Generally done automatically for the user through (make-agenda)."
                       (write-port-map (make-hash-table))
                       (except-port-map (make-hash-table))
                       (schedule (make-schedule))
-                      (time (gettimeofday)))
+                      (time (gettimeofday))
+                      (catch-handler #f)
+                      (pre-unwind-handler #f))
   ;; TODO: document arguments
   "Make a fresh agenda."
   (make-agenda-intern queue prompt
                       read-port-map write-port-map except-port-map
-                      schedule time))
+                      schedule time
+                      catch-handler pre-unwind-handler))
 
 (define (current-agenda-prompt)
   "Get the prompt for the current agenda; signal an error if there isn't one."
@@ -248,13 +254,13 @@ Will produce (0 . 0) instead of a negative number, if needed."
   "Subtract TIME2 from TIME1"
   (time-carry-correct
    (cons (- (car time1) (car time2))
-         (- (cdr time2) (cdr time2)))))
+         (- (cdr time1) (cdr time2)))))
 
 (define (time-plus time1 time2)
   "Add TIME1 and TIME2"
   (time-carry-correct
    (cons (+ (car time1) (car time2))
-         (+ (cdr time2) (cdr time2)))))
+         (+ (cdr time1) (cdr time2)))))
 
 
 (define-record-type <schedule>
@@ -382,6 +388,12 @@ Will produce (0 . 0) instead of a negative number, if needed."
   (lambda ()
     body ...))
 
+(define-syntax-rule (wrap-apply body)
+  "Wrap possibly multi-value function in a procedure, applies all arguments"
+  (lambda args
+    (apply body args)))
+
+
 ;; @@: Do we really want `body ...' here?
 ;;   what about just `body'?
 (define-syntax-rule (run body ...)
@@ -423,6 +435,8 @@ Will produce (0 . 0) instead of a negative number, if needed."
 ;;; =================================
 
 ;; The future's in futures
+;; @@: ... kinda conflicts with ice-9 futures.  Should we rename
+;;   to "futurists"? :)
 
 (define (make-future call-first on-success on-fail on-error)
   ;; TODO: add error stuff here
@@ -450,31 +464,19 @@ Will produce (0 . 0) instead of a negative number, if needed."
    (make-future call-first on-success on-fail on-error)
    when))
 
-(define-syntax-rule (%sync async-request)
+(define-syntax-rule (%8sync async-request)
   "Run BODY asynchronously at a prompt, passing args to make-future.
 
-Pronounced `eight-sync' despite the spelling.
-
-%sync was chosen because (async) was already taken and could lead to
-errors, and this version of asynchronous code uses a prompt, so the `a'
-character becomes a `%' prompt! :)
-
-The % and 8 characters kind of look similar... hence this library's
-name!  (That, and the pun 'eight-synchronous' programming.)
-There are 8sync aliases if you prefer that name."
+Runs things asynchronously (8synchronously?)"
   (abort-to-prompt (current-agenda-prompt)
                    async-request))
 
-(define-syntax-rule (8sync args ...)
-  "Alias for %sync"
-  (%sync args ...))
-
 ;; Async port request and run-request meta-requests
 (define (make-async-request proc)
   "Wrap PROC in an async-request
 
 The purpose of this is to make sure that users don't accidentally
-return the wrong thing via (8sync) and trip themselves up."
+return the wrong thing via (%8sync) and trip themselves up."
   (cons '*async-request* proc))
 
 (define (setup-async-request resume-kont async-request)
@@ -485,7 +487,7 @@ return the wrong thing via (8sync) and trip themselves up."
     ;; TODO: deliver more helpful errors depending on what the user
     ;;   returned
     (_ (throw 'invalid-async-request
-              "Invalid request passed back via an (%sync) procedure."
+              "Invalid request passed back via an (%8sync) procedure."
               async-request))))
 
 (define-syntax-rule (%run body ...)
@@ -517,12 +519,6 @@ return the wrong thing via (8sync) and trip themselves up."
        (lambda ()
          body ...)))))
 
-;; Aliases
-(define-syntax-rule (8run args ...) (%run args ...))
-(define-syntax-rule (8run-at args ...) (%run-at args ...))
-(define-syntax-rule (8run-delay args ...) (%run-delay args ...))
-(define-syntax-rule (8port-request args ...) (%port-request args ...))
-
 
 \f
 ;;; Execution of agenda, and current agenda
@@ -554,10 +550,17 @@ return the wrong thing via (8sync) and trip themselves up."
     ;; TODO: support usecond wait time too
     (match (get-wait-time)
       ((sec . usec)
-       (select (hash-keys (agenda-read-port-map agenda))
-               (hash-keys (agenda-write-port-map agenda))
-               (hash-keys (agenda-except-port-map agenda))
-               sec usec))))
+       (catch 'system-error
+         (lambda ()
+           (select (hash-keys (agenda-read-port-map agenda))
+                   (hash-keys (agenda-write-port-map agenda))
+                   (hash-keys (agenda-except-port-map agenda))
+                   sec usec))
+         (lambda (key . rest-args)
+           (match rest-args
+             ((_ _ _ (EINTR))
+              '(() () ()))
+             (_ (error "Unhandled error in select!" key rest-args))))))))
   (define (get-procs-to-run)
     (define (ports->procs ports port-map)
       (lambda (initial-procs)
@@ -648,6 +651,25 @@ return the wrong thing via (8sync) and trip themselves up."
              (agenda-queue agenda))
             (loop agenda))))))
 
+(define (print-error-and-continue . args)
+  "Frequently used as pre-unwind-handler for agenda"
+  (format (current-error-port) "\n*** Caught exception with arguments: ~s ***\n"
+          args)
+  (display-backtrace (make-stack #t 1 0)
+                     (current-error-port))
+  (newline (current-error-port)))
+
+(define-syntax-rule (maybe-catch-all (catch-handler pre-unwind-handler)
+                                     body ...)
+  (if (or catch-handler pre-unwind-handler)
+      (catch
+        #t
+        (lambda ()
+          body ...)
+        (or catch-handler (lambda _ #f))
+        (or pre-unwind-handler (lambda _ #f)))
+      (begin body ...)))
+
 (define (agenda-run-once agenda)
   "Run once through the agenda, and produce a new agenda
 based on the results"
@@ -656,7 +678,10 @@ based on the results"
      (agenda-prompt-tag agenda)
      (lambda ()
        (parameterize ((%current-agenda agenda))
-         (proc)))
+         (maybe-catch-all
+          ((agenda-catch-handler agenda)
+           (agenda-pre-unwind-handler agenda))
+          (proc))))
      (lambda (kont async-request)
        (setup-async-request kont async-request))))