Initial commit. Improved API to route.rkt (from minimart).

This commit is contained in:
Tony Garnock-Jones 2015-03-02 16:10:11 +00:00
commit 8579ec4151
9 changed files with 2157 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
compiled/

16
Makefile Normal file
View File

@ -0,0 +1,16 @@
PACKAGENAME=prospect
COLLECTS=prospect
all: setup
clean:
find . -name compiled -type d | xargs rm -rf
setup:
raco setup $(COLLECTS)
link:
raco pkg install --link -n $(PACKAGENAME) $$(pwd)
unlink:
raco pkg remove $(PACKAGENAME)

2
info.rkt Normal file
View File

@ -0,0 +1,2 @@
#lang setup/infotab
(define collection 'multi)

47
prospect/canonicalize.rkt Normal file
View File

@ -0,0 +1,47 @@
#lang racket/base
;; Poor-man's hash consing.
(provide canonicalize)
(define canonical-values (make-weak-hash))
(define sentinel (cons #f #f))
(define (canonicalize val)
(define b (hash-ref canonical-values
val
(lambda ()
(define new-b (make-weak-box val))
(hash-set! canonical-values val new-b)
#f)))
(if (not b)
(canonicalize val)
(let ((v (weak-box-value b sentinel)))
(if (eq? v sentinel) (canonicalize val) v))))
(module+ test
(require rackunit)
(define v1 (canonicalize (cons 1 2)))
(let ((v2 (canonicalize (cons 1 2))))
(check-eq? v1 v2))
(collect-garbage)
(check-equal? (hash-count canonical-values) 1)
(let ((v2 (canonicalize (cons 1 2))))
(check-eq? v1 v2))
(set! v1 (canonicalize (cons 1 2)))
(collect-garbage)
(check-equal? (hash-count canonical-values) 1)
(let ((v2 (canonicalize (cons 1 2))))
(check-eq? v1 v2))
(set! v1 #f)
(collect-garbage)
(check-equal? (hash-count canonical-values) 0))

194
prospect/core.rkt Normal file
View File

@ -0,0 +1,194 @@
#lang racket/base
;; Core implementation of Incremental Network Calculus.
(provide )
(require racket/set)
(require racket/match)
(require (only-in racket/list flatten))
(require "functional-queue.rkt")
(require "route.rkt")
(require "patch.rkt")
;; Events ⊃ Patches
;; Actions ⊃ Events
(struct quit () #:prefab)
(struct spawn (behavior boot) #:prefab)
;; Processes (machine states)
(struct process (interests behavior state) #:transparent)
;; A Behavior is a ((Option Event) Any -> Transition): a function
;; mapping an Event (or, in the #f case, a poll signal) and a
;; Process's current state to a Transition.
;;
;; A Transition is either
;; - #f, a signal from a Process that it is inert and need not be
;; scheduled until some Event relevant to it arrives; or,
;; - a (transition Any (Constreeof Action)), a new Process state to
;; be held by its World and a sequence of Actions for the World
;; to take on the transitioning Process's behalf.
(struct transition (state actions) #:transparent)
;; A PID is a Nat.
;; A Label is a PID or 'meta.
;; VM private states
(struct world (next-pid ;; PID
pending-action-queue ;; (Queueof (Cons Label Action))
runnable-pids ;; (Setof PID)
routing-table ;; (Matcherof (Setof Label))
process-table ;; (HashTable PID Process)
environment-interests ;; (Matcherof (set 'meta))
) #:transparent)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (event? x) (or (patch? x)))
(define (action? x) (or (event? x) (spawn? x) (quit? x)))
(define (meta-label? x) (eq? x 'meta))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (ensure-transition v)
(if (or (not v) (transition? v))
v
(raise (exn:fail:contract (format "Expected transition (or #f); got ~v" v)
(current-continuation-marks)))))
(define (clean-transition t)
(and t (transition (transition-state t) (clean-actions (transition-actions t)))))
(define (clean-actions actions)
(filter action? (flatten actions)))
(define (deliver-event e pid p w)
(invoke-process pid
(lambda () ((process-behavior p) e (process-state p)))
(lambda (t) (apply-transition pid t w))
(lambda (exn) (kill-process pid exn w))))
(define (invoke-process pid thunk k-ok k-exn)
(define-values (ok? result)
(with-handlers ([(lambda (exn) #t) (lambda (exn) (values #f exn))])
(values #t (clean-transition
(ensure-transition
(with-continuation-mark 'minimart-process pid (thunk)))))))
(if ok?
(k-ok result)
(k-exn result)))
(define (kill-process pid maybe-exn w)
(define pt (world-process-table w))
(match (hash-ref pt pid)
[#f w]
[(process interests _ _)
(enqueue-actions (struct-copy world w [process-table (hash-remove pt pid)])
pid
(list (patch (matcher-empty) interests)))]))
(define (mark-pid-runnable w pid)
(struct-copy world w [runnable-pids (set-add (world-runnable-pids w) pid)]))
(define (apply-transition pid t w)
(match t
[#f w]
[(transition new-state new-actions)
(let* ((w (transform-process pid w (lambda (p) (struct-copy process p [state new-state])))))
(enqueue-actions (mark-pid-runnable w pid) pid new-actions))]))
(define (transform-process pid w fp)
(define pt (world-process-table w))
(match (hash-ref pt pid)
[#f w]
[p (struct-copy world w [process-table (hash-set pt pid (fp p))])]))
(define (enqueue-actions w label actions)
(struct-copy world w
[pending-action-queue
(queue-append-list (world-pending-action-queue w)
(for/list [(a actions)] (cons label a)))]))
(define (spawn-world . boot-actions)
(spawn world-handle-event
(lambda () (transition (world 0
(make-queue)
(set)
(matcher-empty)
(hash)
(matcher-empty))
'()))))
(define (transition-bind k t0)
(match-define (transition state0 actions0) t0)
(match (k state0)
[#f t0]
[(transition state1 actions1) (transition state1 (cons actions0 actions1))]))
(define (sequence-transitions t0 . steps)
(foldl transition-bind t0 steps))
(define (inert? w)
(and (queue-empty? (world-pending-action-queue w))
(set-empty? (world-runnable-pids w))))
(define (world-handle-event e w)
(if (or e (not (inert? w)))
(sequence-transitions (transition w '())
(inject-event e)
perform-actions
step-children)
(step-children w)))
(define ((inject-event e) w)
(match e
[#f w]
[(? patch? delta)
(enqueue-actions w 'meta (list (lift-patch delta)))]))
(define (perform-actions w)
(for/fold ([wt (transition (struct-copy world w [pending-action-queue (make-queue)]) '())])
((entry (in-list (queue->list (world-pending-action-queue w)))))
(match-define [cons label a] entry)
(transition-bind (perform-action label a) wt)))
(define ((perform-action label a) w)
(match a
[(spawn behavior boot)
(define new-pid (world-next-pid w))
(invoke-process new-pid
boot
(lambda (initial-t)
(match-define (transition initial-state initial-actions) initial-t)
(define new-p (process (matcher-empty) behavior initial-state))
(define new-w
(struct-copy world w
[next-pid (+ new-pid 1)]
[process-table
(hash-set (world-process-table w) new-pid new-p)]))
(mark-pid-runnable (enqueue-actions new-w new-pid initial-actions)
new-pid))
(lambda (exn) (kill-process new-pid exn w)))]
[(quit) (kill-process label #f w)]
[(? patch? delta-orig)
(define p (hash-ref (world-process-table w) label))
(define old-interests (cond
[p (process-interests p)]
[(meta-label? label) (world-environment-interests w)]
[else (matcher-empty)]))
(define old-routing-table (world-routing-table w))
(define delta (limit-patch (label-patch delta-orig label) old-interests))
(define delta-aggregate (compute-patch-aggregate delta label old-routing-table))
(define new-routing-table (apply-patch label old-routing-table delta))
(define affected-pids (compute-affected-pids ...
(define (step-children w)
(define runnable-pids (world-runnable-pids w))
(if (set-empty? runnable-pids)
#f ;; world is inert.
(transition (for/fold ([w (struct-copy world w [runnable-pids (set)])])
[(pid (in-set runnable-pids))]
(define p (hash-ref (world-process-table w) pid (lambda () #f)))
(if (not p) w (deliver-event #f pid p w)))
'())))

View File

@ -0,0 +1,81 @@
#lang racket/base
(provide make-queue
queue?
enqueue
enqueue-all
queue-prepare-for-dequeue
dequeue
list->queue
queue->list
queue-length
queue-empty?
queue-append
queue-append-list
queue-extract)
(struct queue (head tail) #:transparent)
(define (make-queue)
(queue '() '()))
(define (enqueue q v)
(queue (queue-head q)
(cons v (queue-tail q))))
(define (enqueue-all q v)
(queue (queue-head q)
(append (reverse v) (queue-tail q))))
(define (queue-prepare-for-dequeue q)
(if (null? (queue-head q))
(queue (reverse (queue-tail q)) '())
q))
(define (dequeue q)
(let ((q1 (queue-prepare-for-dequeue q)))
(values (car (queue-head q1))
(queue (cdr (queue-head q1)) (queue-tail q1)))))
(define (list->queue xs)
(queue xs '()))
(define (queue->list q)
(append (queue-head q) (reverse (queue-tail q))))
(define (queue-length q)
(+ (length (queue-head q))
(length (queue-tail q))))
(define (queue-empty? q)
(and (null? (queue-head q))
(null? (queue-tail q))))
(define (queue-append q1 q2)
(queue (append (queue-head q1)
(reverse (queue-tail q1))
(queue-head q2))
(queue-tail q2)))
(define (queue-append-list q1 xs)
(queue (queue-head q1)
(append (reverse xs) (queue-tail q1))))
(define (queue-extract q predicate [default-value #f])
(let search-head ((head (queue-head q))
(rejected-head-rev '()))
(cond
((null? head) (let search-tail ((tail (reverse (queue-tail q)))
(rejected-tail-rev '()))
(cond
((null? tail) (values default-value q))
((predicate (car tail)) (values (car tail)
(queue (queue-head q)
(append (reverse (cdr tail))
rejected-tail-rev))))
(else (search-tail (cdr tail) (cons (car tail) rejected-tail-rev))))))
((predicate (car head)) (values (car head)
(queue (append (reverse rejected-head-rev)
(cdr head))
(queue-tail q))))
(else (search-head (cdr head) (cons (car head) rejected-head-rev))))))

138
prospect/patch.rkt Normal file
View File

@ -0,0 +1,138 @@
#lang racket/base
;; Patches to sets of interests
(provide (struct-out patch)
(struct-out observe)
(struct-out at-meta)
lift-patch
drop-patch
limit-patch
apply-patch
compute-patch
pretty-print-patch)
(require racket/set)
(require racket/match)
(require "route.rkt")
(module+ test (require rackunit))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Patches
(struct patch (added removed) #:prefab)
;; Claims, Interests, and Locations
(struct observe (pattern) #:prefab)
(struct at-meta (claim) #:prefab)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (lift-patch p)
(match-define (patch in out) p)
(patch (pattern->matcher #t (at-meta (embedded-matcher in)))
(pattern->matcher #t (at-meta (embedded-matcher out)))))
(define at-meta-proj (compile-projection (at-meta (?!))))
(define (drop-interests pi)
(matcher-project pi at-meta-proj
#:project-success (lambda (v) #t)
#:combiner (lambda (v1 v2) #t)))
(define (drop-patch p)
(match-define (patch in out) p)
(patch (drop-interests in)
(drop-interests out)))
(define (strip-interests g)
(matcher-relabel g (lambda (v) #t)))
(define (label-interests g label)
(matcher-relabel g (lambda (v) label)))
(define (label-patch p label)
(patch (label-interests (patch-added p) label)
(label-interests (patch-removed p) label)))
(define (limit-patch p bound)
(match-define (patch in out) p)
(patch (matcher-subtract in bound #:combiner (lambda (v1 v2) #f))
(matcher-intersect out bound #:combiner (lambda (v1 v2) v1))))
(define (apply-patch base p)
(match-define (patch in out) p)
(matcher-union (matcher-subtract base out) in))
(define (unapply-patch base p)
(match-define (patch in out) p)
(matcher-union (matcher-subtract base in) out))
(define (compose-patch p2 p1) ;; p2 after p1
(match-define (patch in1 out1) p1)
(patch (apply-patch in1 p2)
(unapply-patch out1 p2)))
(define (compute-patch old-base new-base)
(patch (matcher-subtract new-base old-base)
(matcher-subtract old-base new-base)))
(define (pretty-print-patch p)
(match-define (patch in out) p)
(printf "<<<<<<<< Removed:\n")
(pretty-print-matcher out)
(printf "======== Added:\n")
(pretty-print-matcher in)
(printf ">>>>>>>>\n"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(define SP (set 'P))
(define m0 (matcher-empty))
(define ma (pattern->matcher SP 'a))
(define mb (pattern->matcher SP 'b))
(define mc (pattern->matcher SP 'c))
(define mab (matcher-union ma mb))
(define mbc (matcher-union mb mc))
(define m* (pattern->matcher SP ?))
(printf "\nmab:\n")
(void (pretty-print-matcher mab))
(printf "\ncompute-patch ma mb:\n")
(void (pretty-print-patch (compute-patch ma mb)))
(printf "\nlimit-patch m*/m0 mab:\n")
(void (pretty-print-patch (limit-patch (patch m* m0) mab)))
(printf "\nlimit-patch m0/m* mab:\n")
(void (pretty-print-patch (limit-patch (patch m0 m*) mab)))
(printf "\napply mb (limit m*/m0 mab):\n")
(void (pretty-print-matcher (apply-patch mb (limit-patch (patch m* m0) mab))))
(printf "\nlimit mbc/ma ma:\n")
(void (pretty-print-patch (limit-patch (patch mbc ma) ma)))
(printf "\nlimit mab/mc ma:\n")
(void (pretty-print-patch (limit-patch (patch mab mc) ma)))
(printf "\nlimit mc/mab ma:\n")
(void (pretty-print-patch (limit-patch (patch mc mab) ma)))
(printf "\nlift mc/mab:\n")
(void (pretty-print-patch (lift-patch (patch mc mab))))
(printf "\ndrop after lift mc/mab:\n")
(void (pretty-print-patch (drop-patch (lift-patch (patch mc mab)))))
(printf "\ncompose mbc/m0 after lift mc/mab:\n")
(void (pretty-print-patch (compose-patch (patch mbc m0)
(lift-patch (patch mc mab)))))
(printf "\ndrop (compose mbc/m0 after lift mc/mab):\n")
(void (pretty-print-patch (drop-patch (compose-patch (patch mbc m0)
(lift-patch (patch mc mab))))))
)

1469
prospect/route.rkt Normal file

File diff suppressed because it is too large Load Diff

209
prospect/treap.rkt Normal file
View File

@ -0,0 +1,209 @@
#lang racket/base
;; Treaps, which have the lovely property of *canonical representation*.
;;
;; We take care to preserve an additional invariant:
;; - if n is a left child of m, then n's priority <= m's priority, and
;; - if n is a right child of m, then n's priority < m's priority.
;;
;; Further, we explicitly canonicalize N instances, so eq? works to compare treaps by value.
(provide treap?
treap-size
treap-empty
treap-empty?
treap-insert
treap-delete
treap-get
treap-keys
treap-values
treap-to-alist
treap-has-key?
treap-height)
(require racket/set)
(require racket/match)
(require "canonicalize.rkt")
;; (define canonicalize values)
(struct N (key value priority left right) #:transparent
#:methods gen:equal+hash
[(define (equal-proc a b =?)
(match-define (N ak av ap al ar) a)
(match-define (N bk bv bp bl br) b)
(and (eq? al bl)
(eq? ar br)
(= ap bp)
(=? ak bk)
(=? av bv)))
(define (hash-proc a h)
(match-define (N ak av ap al ar) a)
(+ (eq-hash-code al)
(eq-hash-code ar)
(h ap)
(h ak)
(h av)))
(define (hash2-proc a h)
(match-define (N ak av ap al ar) a)
(bitwise-xor (eq-hash-code al)
(eq-hash-code ar)
(h ap)
(h ak)
(h av)))])
(struct L () #:transparent)
(struct treap (order root size) #:transparent)
;; The singleton "empty" leaf sentinel
(define L0 (L))
(define (treap-empty o) (treap o L0 0))
(define (treap-empty? t) (zero? (treap-size t)))
(define (default-priority key)
;; Loosely based on a restriction of murmur32 v3
(define c1 #xcc9e2d51)
(define c2 #x1b873593)
(define r1 15)
(define r2 13)
(define m 5)
(define n #xe6546b64)
(define k (* (equal-hash-code key) c1))
(define hash0 (* c2 (bitwise-ior (arithmetic-shift k r1) (arithmetic-shift k (- 32 r1)))))
(define hash1
(+ n (* m (bitwise-ior (arithmetic-shift hash0 r2) (arithmetic-shift hash0 (- 32 r2))))))
(define hash2
(bitwise-and #xffffffff (* #x85ebca6b (bitwise-xor hash1 (arithmetic-shift hash1 -16)))))
(define hash3
(bitwise-and #xffffffff (* #xc2b2ae35 (bitwise-xor hash2 (arithmetic-shift hash2 -13)))))
(bitwise-xor hash3 (arithmetic-shift hash3 -16)))
(define (treap-insert t key value [priority (default-priority key)])
(match-define (treap order root oldsize) t)
(define newsize (+ oldsize 1)) ;; WARNING: mutated below!
(define newroot
(let walk ((n root))
(match n
[(L)
(canonicalize (N key value priority L0 L0))]
[(N k v p left right)
(case (order key k)
[(<) (match (walk left) [(N K V P l r) (rotate K V P k v p l r right)])]
[(>) (match (walk right) [(N K V P l r) (rotate k v p K V P left l r)])]
[(=)
(set! newsize (- newsize 1)) ;; we are *REPLACING* an existing value
(let merge ((left left) (right right))
(cond
[(priority>= priority left)
(if (priority> priority right)
(canonicalize (N key value priority left right))
(replace-left right (merge left (N-left right))))]
[(priority> priority right)
(replace-right left (merge (N-right left) right))]
[else
(if (priority> (N-priority left) right)
(replace-right left (merge (N-right left) right))
(replace-left right (merge left (N-left right))))]))])])))
(canonicalize (treap order newroot newsize)))
(define (replace-left n x)
(canonicalize
(match n
[(N k v p _ r)
(N k v p x r)])))
(define (replace-right n x)
(canonicalize
(match n
[(N k v p l _)
(N k v p l x)])))
(define (priority> p1 n)
(match n
[(L) #t]
[(N _ _ p2 _ _) (> p1 p2)]))
(define (priority>= p1 n)
(match n
[(L) #t]
[(N _ _ p2 _ _) (>= p1 p2)]))
(define (rotate k1 v1 p1 k2 v2 p2 tl tm tr)
(if (> p1 p2)
(canonicalize (N k1 v1 p1 tl (canonicalize (N k2 v2 p2 tm tr))))
(canonicalize (N k2 v2 p2 (canonicalize (N k1 v1 p1 tl tm)) tr))))
(define (treap-delete t key)
(match-define (treap order root oldsize) t)
(define newsize oldsize)
(define newroot
(let walk ((n root))
(match n
[(L) L0]
[(N k v p left right)
(case (order key k)
[(<) (canonicalize (N k v p (walk left) right))]
[(>) (canonicalize (N k v p left (walk right)))]
[(=)
(set! newsize (- newsize 1)) ;; we found the value to remove
(let merge ((left left) (right right))
(cond
[(L? left) right]
[(L? right) left]
[else
(match-define (N lk lv lp ll lr) left)
(match-define (N rk rv rp rl rr) right)
(canonicalize
(if (< lp rp)
(N lk lv lp ll (merge lr right))
(N rk rv rp (merge left rl) rr)))]))])])))
(canonicalize (treap order newroot newsize)))
(define (treap-get t key [on-missing (lambda () #f)])
(define order (treap-order t))
(let walk ((n (treap-root t)))
(match n
[(L) (on-missing)]
[(N k v _ left right)
(case (order key k)
[(<) (walk left)]
[(>) (walk right)]
[(=) v])])))
(define (treap-keys t #:empty-set [empty-set (set)])
(let walk ((n (treap-root t)) (acc empty-set))
(match n
[(L) acc]
[(N k _ _ left right) (walk left (walk right (set-add acc k)))])))
(define (treap-values t)
(let walk ((n (treap-root t)) (acc '()))
(match n
[(L) acc]
[(N k _ _ left right) (walk left (cons k (walk right acc)))])))
(define (treap-to-alist t)
(let walk ((n (treap-root t)) (acc '()))
(match n
[(L) acc]
[(N k v _ left right) (walk left (cons (cons k v) (walk right acc)))])))
(define (treap-has-key? t key)
(define order (treap-order t))
(let walk ((n (treap-root t)))
(match n
[(L) #f]
[(N k v _ left right)
(case (order key k)
[(<) (walk left)]
[(>) (walk right)]
[(=) #t])])))
(define (treap-height t)
(let walk ((n (treap-root t)))
(match n
[(L) 0]
[(N _ _ _ l r) (+ 1 (max (walk l) (walk r)))])))