X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=loopy.scm;h=01c0b52110d1615648242b9672259479bf7ef350;hb=1b92ec2ba2bcb0aed0e519c558cd2d0a848d8701;hp=603a75a47e19afc1be2208530ce05b0d9a078a26;hpb=5766ad69cacb52a8a0705ae9279ec64f45e9aa31;p=8sync.git diff --git a/loopy.scm b/loopy.scm index 603a75a..01c0b52 100644 --- a/loopy.scm +++ b/loopy.scm @@ -1,4 +1,4 @@ -(define-module (loopy agenda) +(define-module (eightsync agenda) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) #:use-module (srfi srfi-9 gnu) @@ -33,11 +33,18 @@ schedule-segments-split schedule-extract-until! add-segments-contents-to-queue! + %sync 8sync %sync-at 8sync-at %sync-delay 8sync-delay + make-run-request run-request? run-request-proc run-request-when - run-it wrap run run-at delay + + make-port-request port-request? + port-request-port + port-request-read port-request-write port-request-except + + run-it wrap run run-at run-delay %current-agenda start-agenda agenda-run-once)) @@ -77,6 +84,9 @@ (time agenda-time)) (define (make-async-prompt-tag) + "Make an async prompt tag for an agenda. + +Generally done automatically for the user through (make-agenda)." (make-prompt-tag "prompt")) (define* (make-agenda #:key @@ -87,10 +97,21 @@ (except-port-map (make-hash-table)) (schedule (make-schedule)) (time (gettimeofday))) + ;; TODO: document arguments + "Make a fresh agenda." (make-agenda-intern queue prompt read-port-map write-port-map except-port-map schedule time)) +(define (current-agenda-prompt) + "Get the prompt for the current agenda; signal an error if there isn't one." + (let ((current-agenda (%current-agenda))) + (if (not current-agenda) + (throw + 'no-current-agenda + "Can't get current agenda prompt if there's no current agenda!") + (agenda-prompt-tag current-agenda)))) + ;;; Schedule @@ -311,7 +332,7 @@ Will produce (0 . 0) instead of a negative number, if needed." ;;; Request to run stuff ;;; ==================== -(define-immutable-record-type +(define-record-type (make-run-request proc when) run-request? (proc run-request-proc) @@ -326,6 +347,8 @@ Will produce (0 . 0) instead of a negative number, if needed." (lambda () body ...)) +;; @@: Do we really want `body ...' here? +;; what about just `body'? (define-syntax-rule (run body ...) "Run everything in BODY but wrap in a convenient procedure" (make-run-request (wrap body ...) #f)) @@ -334,15 +357,101 @@ Will produce (0 . 0) instead of a negative number, if needed." "Run BODY at WHEN" (make-run-request (wrap body ...) when)) +;; @@: Is it okay to overload the term "delay" like this? +;; Would `run-in' be better? (define-syntax-rule (run-delay body ... delay-time) "Run BODY at DELAY-TIME time from now" (make-run-request (wrap body ...) (tdelta delay-time))) -(define (delay run-request delay-time) - "Delay a RUN-REQUEST by DELAY-TIME" - (set-field run-request - (run-request-when) - (tdelta delay-time))) + +;; A request to set up a port with at least one of read, write, except +;; handling processes + +(define-record-type + (make-port-request-intern port read write except) + port-request? + (port port-request-port) + (read port-request-read) + (write port-request-write) + (except port-request-except)) + +(define* (make-port-request port #:key read write except) + (if (not (or read write except)) + (throw 'no-port-handler-given "No port handler given.\n")) + (make-port-request-intern port read write except)) + + + +;;; Asynchronous escape to run things +;;; ================================= + +;; The future's in futures + +(define (make-future call-first on-success on-fail on-error) + ;; TODO: add error stuff here + (lambda () + (let ((call-result (call-first))) + ;; queue up calling the + (run (on-success call-result))))) + +(define (agenda-on-error agenda) + (const #f)) + +(define (agenda-on-fail agenda) + (const #f)) + +(define* (request-future call-first on-success + #:key + (agenda (%current-agenda)) + (on-fail (agenda-on-fail agenda)) + (on-error (agenda-on-error agenda)) + (when #f)) + ;; TODO: error handling + ;; do we need some distinction between expected, catchable errors, + ;; and unexpected, uncatchable ones? Probably...? + (make-run-request + (make-future call-first on-success on-fail on-error) + when)) + +(define-syntax-rule (%sync body args ...) + "Run BODY asynchronously at a prompt, passing args to make-future. + +Pronounced `async' 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! (There are 8sync aliases if you prefer that name.)" + (abort-to-prompt (current-agenda-prompt) + (wrap body) + args ...)) + +(define-syntax-rule (%sync-at body when args ...) + (abort-to-prompt (current-agenda-prompt) + (wrap body) + #:when when + args ...)) + +(define-syntax-rule (%sync-delay body delay-time args ...) + (abort-to-prompt (current-agenda-prompt) + (wrap body) + #:when (tdelta delay-time) + args ...)) + +(define-syntax-rule (8sync args ...) + "Alias for %sync" + (%sync args ...)) + +(define-syntax-rule (8sync-at args ...) + "Alias for %sync-at" + (%sync-at args ...)) + +(define-syntax-rule (8sync-delay args ...) + "Alias for %sync-delay" + (8sync-delay args ...)) + ;;; Execution of agenda, and current agenda @@ -351,6 +460,7 @@ Will produce (0 . 0) instead of a negative number, if needed." (define %current-agenda (make-parameter #f)) (define (update-agenda-from-select! agenda) + "Potentially (select) on ports specified in agenda, adding items to queue" (define (hash-keys hash) (hash-map->list (lambda (k v) k) hash)) (define (get-wait-time) @@ -424,11 +534,24 @@ Will produce (0 . 0) instead of a negative number, if needed." (update-agenda) agenda)) +(define (agenda-handle-port-request! agenda port-request) + "Update an agenda for a port-request" + (define (handle-selector request-selector port-map-selector) + (if (request-selector port-request) + (hash-set! (port-map-selector agenda) + (port-request-port port-request) + (request-selector port-request)))) + (handle-selector port-request-read agenda-read-port-map) + (handle-selector port-request-write agenda-write-port-map) + (handle-selector port-request-except agenda-except-port-map)) + (define* (start-agenda agenda #:key stop-condition (get-time gettimeofday) (handle-ports update-agenda-from-select!)) + ;; TODO: Document fields + "Start up the AGENDA" (let loop ((agenda agenda)) (let ((agenda ;; @@: Hm, maybe here would be a great place to handle @@ -463,8 +586,9 @@ based on the results" (agenda-prompt-tag agenda) (lambda () (proc)) - ;; TODO - (lambda (k) k))) + (lambda* (resume-with please-run-this . args) + (apply request-future please-run-this resume-with + args)))) (let ((queue (agenda-queue agenda)) (next-queue (make-q))) @@ -488,19 +612,21 @@ based on the results" (schedule-at! request-time (run-request-proc run-request))) (#f (enq! next-queue (run-request-proc run-request)))))))) + (define (handle-individual result) + (match result + ((? run-request? new-proc) + (enqueue new-proc)) + ((? port-request? port-request) + (agenda-handle-port-request! agenda port-request)) + ;; do nothing + (_ #f))) ;; @@: We might support delay-wrapped procedures here (match proc-result ;; TODO: replace procedure with something that indicates ;; intent to run. Use a (run foo) procedure - ((? run-request? new-proc) - (enqueue new-proc)) - (((? run-request? new-procs) ...) - (for-each - (lambda (new-proc) - (enqueue new-proc)) - new-procs)) - ;; do nothing - (_ #f)))) + ((results ...) + (for-each handle-individual results)) + (one-result (handle-individual one-result))))) ;; TODO: Alternately, we could return the next-queue ;; along with changes to be added to the schedule here? ;; Return new agenda, with next queue set