syndicate-2017/prospect/actor.rkt

497 lines
18 KiB
Racket
Raw Normal View History

2015-11-23 22:33:12 +00:00
#lang racket/base
(provide actor
2015-11-23 22:33:12 +00:00
;; network
;; background
2015-11-25 15:42:13 +00:00
state
2015-11-23 22:33:12 +00:00
until
forever
assert!
retract!
send!
quit!
;; forall
;;----------------------------------------
(struct-out actor-state)
)
(require (for-syntax racket/base))
(require "support/dsl.rkt")
(define&provide-dsl-helper-syntaxes "state/until/forever form"
[on
assert
track
asserted
retracted
message
rising-edge
exists
])
(require (for-syntax racket/match))
(require (for-syntax racket/list))
(require (for-syntax syntax/parse))
(require (for-syntax syntax/stx))
(require racket/set)
(require racket/match)
(require "core.rkt")
(require "route.rkt")
(require "mux.rkt")
2015-11-23 22:33:12 +00:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2015-11-25 15:42:13 +00:00
;; Actor State
;; A Variables is a (Vectorof Any), storing the explicit and implicit
;; state variables of an actor, including tracked and implicit
;; aggregates.
;; A Script is a (Variables -> Variables). It is to be executed inside
;; the special syndicate-hll prompt, and so may have Instruction
;; side-effects.
;; An Instruction is one of
;; - (patch-instruction Patch (Void -> Instruction))
;; - (action-instruction Action (Void -> Instruction))
2015-11-25 15:42:13 +00:00
;; - (quit-instruction (Option Exn))
;; - (spawn-instruction LinkageKind (Symbol Symbol -> Spawn) (Void -> Instruction))
2015-11-25 15:42:13 +00:00
;; - (script-complete-instruction Variables)
;; and represents a side-effect for an actor to take in its
;; interactions with the outside world.
;;
;; A LinkageKind is one of
;; - 'call, a blocking, exception-linked connection
;; - 'actor, a non-blocking, non-exception-linked connection
;; - 'network, a non-blocking, nested, non-exception-linked connection
;;
;; Patch Instructions are issued when the actor uses `assert!` and
;; `retract!`. Action instructions are issued when the actor uses
;; `do!`, and quit instructions when `quit!` is called.
2015-11-25 15:42:13 +00:00
;; Script-complete instructions are automatically issued when a Script
;; terminates successfully.
;;
;; Spawn instructions are issued when `actor`, `network`, and `state`
;; are used, directly or indirectly. (TODO: `background`?) The
;; spawn-action-producing function is given the IDs of the spawned and
;; spawning actors, and is to return an action which spawns the new
;; actor, which in turn engages in the appropriate linkage protocol
;; with the spawning actor. The (Void -> Instruction) continuation is
;; released when the spawned actor terminates (for blocking variants)
;; or immediately following the spawn (for non-blocking variants).
2015-11-25 15:42:13 +00:00
;;
;; (Background is done differently, with a new continuation for the
;; background script, and a self-send to activate it. (TODO))
;;
(struct patch-instruction (patch k) #:transparent)
(struct action-instruction (action k) #:transparent)
2015-11-25 15:42:13 +00:00
(struct quit-instruction (maybe-exn) #:transparent)
(struct spawn-instruction (linkage-kind action-fn k) #:transparent)
2015-11-25 15:42:13 +00:00
(struct script-complete-instruction (variables) #:transparent)
;; An ActorState is an (actor-state ... as below), describing the
;; state of an HLL actor.
;;
(struct actor-state (continuation-table ;; (Hashtable Symbol (Variables Any ... -> Instruction))
caller-id ;; Symbol
self-id ;; Symbol
variables ;; Variables
pending-patch ;; (Option Patch) - aggregate patch being accumulated
mux ;; Mux
2015-11-25 15:42:13 +00:00
)
#:prefab)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Linkage protocol
;;
;; Linkages are used to both propagate values from callee to caller
;; and to monitor callee presence for exception-propagation.
;; - assertion: LinkActive
;; - message: LinkResult
;; - role: Caller
;; Monitors LinkActive to detect termination of the Callee,
;; normal or abnormal. If LinkResult is received before
;; LinkActive vanishes, termination was normal; otherwise, it
;; was abnormal.
;; - role: Callee
;; Asserts LinkActive while it runs. Should send LinkResult
;; before termination to indicate success and communicate values
;; to Caller.
;;
;; A LinkActive is a (link-active Symbol Symbol), describing an
;; ongoing relationship between the indicated caller and callee.
(struct link-active (caller-id callee-id) #:prefab)
;;
;; A LinkResult is a (link-result Symbol Symbol (Listof Any)),
;; describing the final values yielded by a callee to its caller.
(struct link-result (caller-id callee-id values) #:prefab) ;; message
;; Projection for observing LinkActive.
(define link-active-projection (compile-projection (link-active ? (?!))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Producing Instruction side-effects
(define prompt (make-continuation-prompt-tag 'syndicate-hll))
;; (Any ... -> Nothing) -> (Any ... -> Instruction)
(define (reply-to k)
(lambda reply-values
(call-with-continuation-prompt (lambda ()
(with-handlers [((lambda (e) #t)
(lambda (exn)
(call-in-raw-context/abort
(lambda () (quit-instruction exn)))))]
(apply k reply-values)
(error 'reply-to "Script returned directly")))
prompt)))
;; (-> Instruction) -> Nothing
(define (call-in-raw-context/abort proc)
(abort-current-continuation prompt proc))
;; ((Any ... -> Instruction) -> Instruction)
(define (call-in-raw-context proc)
(call-with-composable-continuation
(lambda (k) (abort-current-continuation prompt (lambda () (proc (reply-to k)))))
prompt))
;; Returns void
(define (assert! P)
(call-in-raw-context
(lambda (k) (patch-instruction (patch (pattern->matcher #t P) (matcher-empty)) k))))
;; Returns void
(define (retract! P)
(call-in-raw-context
(lambda (k) (patch-instruction (patch (matcher-empty) (pattern->matcher #t P)) k))))
;; Returns void
(define (send! M) (do! (message M)))
;; Returns void
(define (do! A)
2015-11-25 15:42:13 +00:00
(call-in-raw-context
(lambda (k) (action-instruction A k))))
2015-11-25 15:42:13 +00:00
;; Does not return
(define (quit! [maybe-exn #f])
(call-in-raw-context/abort
(lambda () (quit-instruction maybe-exn))))
2015-11-23 22:33:12 +00:00
2015-11-25 15:42:13 +00:00
;; Returns new variables, plus values from spawned actor if any.
(define (spawn! linkage-kind action-fn)
2015-11-25 15:42:13 +00:00
(call-in-raw-context
(lambda (k) (spawn-instruction linkage-kind action-fn k))))
2015-11-25 15:42:13 +00:00
;; Syntax for spawning a 'call-linked actor.
2015-11-23 22:33:12 +00:00
(define-syntax (state stx)
(syntax-parse stx
[(_ #:init [I ...] [#:collect [(id init) ...] O ...] [E Oe ...] ...)
2015-11-25 15:42:13 +00:00
(expand-state 'call #'(I ...) #'(id ...) #'(init ...) #'(O ...) #'([E Oe ...] ...))]
2015-11-23 22:33:12 +00:00
[(_ [#:collect [(id init) ...] O ...] [E Oe ...] ...)
2015-11-25 15:42:13 +00:00
(expand-state 'call #'() #'(id ...) #'(init ...) #'(O ...) #'([E Oe ...] ...))]
2015-11-23 22:33:12 +00:00
[(_ #:init [I ...] [O ...] [E Oe ...] ...)
2015-11-25 15:42:13 +00:00
(expand-state 'call #'(I ...) #'() #'() #'(O ...) #'([E Oe ...] ...))]
2015-11-23 22:33:12 +00:00
[(_ [O ...] [E Oe ...] ...)
2015-11-25 15:42:13 +00:00
(expand-state 'call #'() #'() #'() #'(O ...) #'([E Oe ...] ...))]))
2015-11-23 22:33:12 +00:00
(define-syntax (named-binding-values stx)
(syntax-parse stx
[(_ #:collect [(id init) ...] O ...) #'(values id ...)]
[(_ O ...) #'(void)]))
2015-11-25 15:42:13 +00:00
;; Sugar
2015-11-23 22:33:12 +00:00
(define-syntax-rule (until E O ...)
(state [O ...] [E (named-binding-values O ...)]))
2015-11-23 22:33:12 +00:00
2015-11-25 15:42:13 +00:00
;; Sugar
2015-11-23 22:33:12 +00:00
(define-syntax-rule (forever O ...)
(state [O ...]))
;; Spawn actors with 'actor linkage
(define-syntax (actor stx)
(syntax-parse stx
[(_ I ...)
(expand-state 'actor #'(I ... (quit!)) #'() #'() #'() #'())]))
2015-11-23 22:33:12 +00:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2015-11-25 15:42:13 +00:00
;; Main behavior of HLL actors
2015-11-23 22:33:12 +00:00
;; Special mux label used to track ad-hoc assertions
;; TODO: Revisit this, it is a bit ugly
(define *adhoc-label* -1)
;; Special mux label used to track linkage between actors.
;; TODO: Revisit this, it is a bit ugly
(define *linkage-label* -2)
2015-11-25 15:42:13 +00:00
;; Behavior
(define (generic-actor-behavior e s)
2015-11-23 22:33:12 +00:00
(match e
[(? patch/removed? p)
(define continuation-table (actor-state-continuation-table s))
(define quit?
2015-11-25 15:42:13 +00:00
(for/or [(callee-id (matcher-project/set/single (patch-removed p) link-active-projection))]
(hash-has-key? continuation-table callee-id)))
2015-11-23 22:33:12 +00:00
(if quit? ;; TODO: raise exception instead? Signal the cause of the quit somehow?
(quit)
#f)]
2015-11-25 15:42:13 +00:00
[(message (link-result _ callee-id reply-values))
(invoke-stored-continuation s callee-id reply-values)]
2015-11-23 22:33:12 +00:00
[_ #f]))
2015-11-25 15:42:13 +00:00
;; ActorState Symbol (Variables Any ... -> Instruction) -> ActorState
(define (store-continuation s callee-id get-next-instr)
2015-11-23 22:33:12 +00:00
(struct-copy actor-state s
[continuation-table
(hash-set (actor-state-continuation-table s)
2015-11-25 15:42:13 +00:00
callee-id
2015-11-23 22:33:12 +00:00
get-next-instr)]))
2015-11-25 15:42:13 +00:00
;; ActorState Symbol (Listof Any) -> Transition
(define (invoke-stored-continuation s callee-id reply-values)
2015-11-23 22:33:12 +00:00
(define continuation-table (actor-state-continuation-table s))
2015-11-25 15:42:13 +00:00
(define continuation (hash-ref continuation-table callee-id #f))
(define new-table (hash-remove continuation-table callee-id))
2015-11-23 22:33:12 +00:00
(handle-actor-syscall (transition (struct-copy actor-state s [continuation-table new-table])
'())
(apply continuation (actor-state-variables s) reply-values)))
;; ActorState -> Transition
(define (perform-pending-patch s)
(transition (struct-copy actor-state s [pending-patch #f]) (actor-state-pending-patch s)))
;; Label Patch -> ActorState -> Transition
(define ((extend-pending-patch label p) s)
(define-values (new-mux _label _p p-aggregate)
(mux-update-stream (actor-state-mux s) label p))
(define p0 (actor-state-pending-patch s))
(define new-pending-patch (if p0 (patch-seq p0 p-aggregate) p-aggregate))
(transition (struct-copy actor-state s
[pending-patch new-pending-patch]
[mux new-mux])
'()))
2015-11-25 15:42:13 +00:00
;; ActorState Script -> Transition
2015-11-23 22:33:12 +00:00
(define (run-script s script)
(handle-actor-syscall (transition s '())
((reply-to (lambda (dummy)
2015-11-25 15:42:13 +00:00
(define new-variables (script (actor-state-variables s)))
(call-in-raw-context/abort
(lambda ()
(script-complete-instruction new-variables)))))
2015-11-23 22:33:12 +00:00
(void))))
2015-11-25 15:42:13 +00:00
;; Behavior -> Behavior
2015-11-23 22:33:12 +00:00
(define (compose-ongoing-handler ongoing-handler)
(lambda (e s)
(match (ongoing-handler e s)
2015-11-25 15:42:13 +00:00
[#f (generic-actor-behavior e s)]
[t (transition-bind (lambda (s) (generic-actor-behavior e s)) t)])))
2015-11-23 22:33:12 +00:00
2015-11-25 15:42:13 +00:00
;; Transition Instruction -> Transition
2015-11-23 22:33:12 +00:00
(define (handle-actor-syscall t instr)
(match instr
[(patch-instruction p get-next-instr)
(handle-actor-syscall (sequence-transitions t
(extend-pending-patch *adhoc-label* p))
2015-11-23 22:33:12 +00:00
(get-next-instr (void)))]
[(action-instruction a get-next-instr)
(handle-actor-syscall (sequence-transitions t
perform-pending-patch
(lambda (s) (transition s a)))
2015-11-23 22:33:12 +00:00
(get-next-instr (void)))]
[(quit-instruction maybe-exn)
(sequence-transitions t
perform-pending-patch
(lambda (s) (quit #:exception maybe-exn)))]
[(spawn-instruction linkage-kind action-fn get-next-instr)
(define blocking? (eq? linkage-kind 'call))
(define next-t
(sequence-transitions t
perform-pending-patch
(lambda (s)
(define callee-id (gensym 'actor))
(transition (if blocking?
(store-continuation s callee-id get-next-instr)
s)
(action-fn callee-id (actor-state-self-id s))))))
(if blocking?
next-t
(handle-actor-syscall next-t (get-next-instr (void))))]
2015-11-23 22:33:12 +00:00
[(script-complete-instruction new-variables)
(sequence-transitions t
;; NB: Does not perform-pending-patch here.
;; Instead, the script runner will now
;; update ongoing subscriptions and
;; incorporate the pending patch into that
;; process.
(lambda (s)
(transition (struct-copy actor-state s [variables new-variables])
'())))]))
2015-11-23 22:33:12 +00:00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2015-11-25 15:42:13 +00:00
;; Compilation of HLL actors
2015-11-23 22:33:12 +00:00
(begin-for-syntax
2015-11-25 15:42:13 +00:00
(define (expand-state linkage-kind init-actions binding-names binding-inits ongoings edges)
2015-11-23 22:33:12 +00:00
(define state-variable-init-exps binding-inits)
(define (allocate-state-variable! init-exp)
(set! state-variable-init-exps
(append state-variable-init-exps (list init-exp)))
(- (length state-variable-init-exps) 1))
(for ((edge (syntax->list edges)))
(printf "~v\n" (syntax->datum edge)))
2015-11-23 22:33:12 +00:00
(define action-fn-stx
#`(lambda (self-id caller-id)
(<spawn>
(lambda ()
;; ActorState -> Transition
(define (update-ongoing-interests s)
blah blah)
(define (behavior e s)
(log-error "TODO: event handling"))
(define initial-state
(actor-state (hasheq)
caller-id
self-id
(vector #,@state-variable-init-exps)
#f
(mux)))
(define (subscribe-to-linkage s)
(define sub-to-callees
(patch-seq (sub (link-active self-id ?))
(sub (link-result self-id ? ?))))
(define initial-subs
#,(if (eq? linkage-kind 'call)
#`(patch-seq sub-to-callees
(assert (link-active caller-id self-id)))
#`sub-to-callees))
(transition s initial-subs))
(define (run-init-actions s)
(run-script s (lambda (vs)
#,@init-actions
vs)))
(list behavior
(sequence-transitions (transition initial-state '())
subscribe-to-linkage
run-init-actions))))))
#`(spawn! '#,linkage-kind #,action-fn-stx))
2015-11-23 22:33:12 +00:00
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2015-11-25 15:42:13 +00:00
;; HLL pattern analysis
2015-11-23 22:33:12 +00:00
(begin-for-syntax
(define (dollar-id? stx)
(and (identifier? stx)
(char=? (string-ref (symbol->string (syntax-e stx)) 0) #\$)))
(define (undollar stx)
(and (dollar-id? stx)
(datum->syntax stx (string->symbol (substring (symbol->string (syntax-e stx)) 1)))))
;; Syntax -> (Values Projection AssertionSetPattern MatchPattern (ListOf Identifier))
(define (analyze-pattern pat-stx)
(syntax-case pat-stx ($ quasiquote unquote quote)
;; Extremely limited support for quasiquoting and quoting
[(quasiquote (unquote p)) (analyze-pattern #'p)]
[(quasiquote (p ...)) (analyze-pattern #'(list (quasiquote p) ...))]
[(quasiquote p) (values #''p #''p #''p '())]
[(quote p) (values #''p #''p #''p '())]
[$v
(dollar-id? #'$v)
(with-syntax [(v (undollar #'$v))]
(values #'(?!)
#'?
#'v
(list #'v)))]
[($ v p)
(let ()
(define-values (pr g m bs) (analyze-pattern #'p))
(when (not (null? bs))
(raise-syntax-error #f "nested bindings not supported" pat-stx))
(values #`(?! #,pr)
g
#`(and v #,m)
(list #'v)))]
[(ctor p ...)
(let ()
(define parts (if (identifier? #'ctor) #'(p ...) #'(ctor p ...)))
(define-values (pr g m bs)
(for/fold [(pr '()) (g '()) (m '()) (bs '())] [(p (syntax->list parts))]
(define-values (pr1 g1 m1 bs1) (analyze-pattern p))
(values (cons pr1 pr)
(cons g1 g)
(cons m1 m)
(append bs1 bs))))
(if (identifier? #'ctor)
(values (cons #'ctor (reverse pr))
(cons #'ctor (reverse g))
(cons #'ctor (reverse m))
bs)
(values (reverse pr)
(reverse g)
(reverse m)
bs)))]
[non-pair
(if (and (identifier? #'non-pair)
(free-identifier=? #'non-pair #'_))
(values #'?
#'?
#'_
'())
(values #'non-pair
#'non-pair
#'(== non-pair)
'()))]))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(require racket/pretty (for-syntax racket/pretty))
(define (expand-and-print stx)
(pretty-print (syntax->datum (expand stx))))
(begin-for-syntax
(define (analyze-and-print pat-stx)
(let-values (((pr g m bs) (analyze-pattern pat-stx)))
(pretty-print `((pr ,(map syntax->datum pr))
(g ,(map syntax->datum g))
(m ,(map syntax->datum m))
(bs ,(map syntax->datum bs))))))
(analyze-and-print #'`(hello ,$who)))
(expand-and-print
#'(actor
(until (rising-edge (= count 10))
2015-11-23 22:33:12 +00:00
#:collect [(count 0)]
(on (message `(hello ,$who))
(println "Got hello: ~a" who)
(+ count 1))))))