Improve documentation and comments.
This commit is contained in:
parent
c028d852d0
commit
49b6d0dfb7
6
TODO
6
TODO
|
@ -8,3 +8,9 @@ It feels like those lowest-level drivers are listening for *demand*
|
||||||
i.e. they're listening for presence and are then acting to supply such
|
i.e. they're listening for presence and are then acting to supply such
|
||||||
demand. Think about the relationships between presence (both positive
|
demand. Think about the relationships between presence (both positive
|
||||||
and negative), and interfacing to ad-hoc sources and sinks.
|
and negative), and interfacing to ad-hoc sources and sinks.
|
||||||
|
|
||||||
|
### Old, possibly-still-relevant TODOs from os.rkt
|
||||||
|
|
||||||
|
- is timeout really primitive? If so, isn't presence primitive?
|
||||||
|
- what about metatimeout?
|
||||||
|
- enforce user-mode restrictions
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Pretty hex dump output of a Bytes.
|
||||||
|
|
||||||
(provide dump-bytes!)
|
(provide dump-bytes!)
|
||||||
|
|
||||||
|
;; Exact Exact -> String
|
||||||
|
;; Returns the "0"-padded, width-digit hex representation of n
|
||||||
(define (hex width n)
|
(define (hex width n)
|
||||||
(define s (number->string n 16))
|
(define s (number->string n 16))
|
||||||
(define slen (string-length s))
|
(define slen (string-length s))
|
||||||
|
@ -10,6 +13,8 @@
|
||||||
((= slen width) s)
|
((= slen width) s)
|
||||||
((> slen width) (substring s 0 width))))
|
((> slen width) (substring s 0 width))))
|
||||||
|
|
||||||
|
;; Bytes Exact -> Void
|
||||||
|
;; Prints a pretty hex/ASCII dump of bs on (current-output-port).
|
||||||
(define (dump-bytes! bs requested-count)
|
(define (dump-bytes! bs requested-count)
|
||||||
(define count (min requested-count (bytes-length bs)))
|
(define count (min requested-count (bytes-length bs)))
|
||||||
(define clipped (subbytes bs 0 count))
|
(define clipped (subbytes bs 0 count))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
;; Emacs indent settings
|
||||||
(mapcar #'(lambda (x) (put x 'scheme-indent-function 1))
|
(mapcar #'(lambda (x) (put x 'scheme-indent-function 1))
|
||||||
'(transition extend-transition
|
'(transition extend-transition
|
||||||
subscribe subscribe/fresh unsubscribe
|
subscribe subscribe/fresh unsubscribe
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Trivial demonstration of an os-big-bang style virtual machine.
|
||||||
|
;; Engages in various I/O and timer operations.
|
||||||
|
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
(require racket/port)
|
(require racket/port)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Utilities for testing os-big-bang worlds.
|
||||||
|
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
(require rackunit)
|
(require rackunit)
|
||||||
|
@ -11,6 +12,10 @@
|
||||||
(transition (transition-state t) (transition-actions t)) ;; autoflattens
|
(transition (transition-state t) (transition-actions t)) ;; autoflattens
|
||||||
(transition t '()))) ;; wrap for convenient comparison
|
(transition t '()))) ;; wrap for convenient comparison
|
||||||
|
|
||||||
|
;; (on-message ...) World Any World ListOf<Action> -> Void
|
||||||
|
;; Passes the given message to the given message-handler, with the
|
||||||
|
;; given initial-w. Compares the resulting world and list of actions
|
||||||
|
;; to the expected world and list of actions using check-equal?.
|
||||||
(define (check-message-handler mh initial-w message final-w expected-actions)
|
(define (check-message-handler mh initial-w message final-w expected-actions)
|
||||||
(match-define (on-message pattern handler) mh)
|
(match-define (on-message pattern handler) mh)
|
||||||
(check-true (pattern message) "Message-handler pattern did not match message provided")
|
(check-true (pattern message) "Message-handler pattern did not match message provided")
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
; Evented userland for os.rkt. Maintains persistent subscriptions.
|
||||||
; Evented userland for os.rkt
|
|
||||||
|
|
||||||
(require racket/set)
|
(require racket/set)
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Trivial demonstration of a raw os.rkt virtual machine.
|
||||||
|
|
||||||
(require "os.rkt")
|
(require "os.rkt")
|
||||||
(require racket/pretty)
|
(require racket/pretty)
|
||||||
|
|
28
os-timer.rkt
28
os-timer.rkt
|
@ -1,22 +1,42 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Timer drivers for os.rkt
|
||||||
|
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
(require "os-big-bang.rkt")
|
(require "os-big-bang.rkt")
|
||||||
(require "os-udp.rkt")
|
|
||||||
|
|
||||||
(provide (struct-out set-timer)
|
(provide (struct-out set-timer)
|
||||||
(struct-out timer-expired)
|
(struct-out timer-expired)
|
||||||
timer-driver
|
timer-driver
|
||||||
timer-relay)
|
timer-relay)
|
||||||
|
|
||||||
|
;; (set-timer Any Number Boolean)
|
||||||
|
;; The timer driver listens for messages of this type, and when it
|
||||||
|
;; hears one, sets an alarm that will later send a corresponding
|
||||||
|
;; timer-expired message.
|
||||||
(struct set-timer (label msecs relative?) #:prefab)
|
(struct set-timer (label msecs relative?) #:prefab)
|
||||||
|
|
||||||
|
;; (timer-expired Any Number)
|
||||||
|
;; Message sent by the timer driver upon expiry of a timer. Contains
|
||||||
|
;; the label specified in the corresponding set-timer message, and
|
||||||
|
;; also the current absolute time from the outside world.
|
||||||
(struct timer-expired (label msecs) #:prefab)
|
(struct timer-expired (label msecs) #:prefab)
|
||||||
|
|
||||||
;; Something like this should be part of racket
|
;; Note that (set-timer 'current-time 0 #f) causes an immediate reply
|
||||||
|
;; of (timer-expired 'current-time (current-inexact-milliseconds)),
|
||||||
|
;; which can be used for an event-oriented interface to reading the
|
||||||
|
;; system clock.
|
||||||
|
|
||||||
|
;; Racket's alarm-evt is almost the right design for timeouts: its
|
||||||
|
;; synchronisation value should be the (or some) value of the clock
|
||||||
|
;; after the asked-for time. That way it serves as timeout and
|
||||||
|
;; clock-reader in one.
|
||||||
(define (timer-evt msecs relative?)
|
(define (timer-evt msecs relative?)
|
||||||
(wrap-evt (alarm-evt (if relative? (+ (current-inexact-milliseconds) msecs) msecs))
|
(wrap-evt (alarm-evt (if relative? (+ (current-inexact-milliseconds) msecs) msecs))
|
||||||
(lambda (_) (current-inexact-milliseconds))))
|
(lambda (_) (current-inexact-milliseconds))))
|
||||||
|
|
||||||
|
;; [Symbol] -> BootK
|
||||||
|
;; Process for mapping this-level timer requests to meta-level timer
|
||||||
|
;; events and back.
|
||||||
(define (timer-driver [self-id 'timer-driver])
|
(define (timer-driver [self-id 'timer-driver])
|
||||||
(os-big-bang 'no-state
|
(os-big-bang 'no-state
|
||||||
(subscribe 'timer-setter
|
(subscribe 'timer-setter
|
||||||
|
@ -32,6 +52,10 @@
|
||||||
(unsubscribe label)
|
(unsubscribe label)
|
||||||
(send-message (timer-expired reply-label now)))])))]))))
|
(send-message (timer-expired reply-label now)))])))]))))
|
||||||
|
|
||||||
|
;; [Symbol] -> BootK
|
||||||
|
;; Process for mapping this-level timer requests to meta-level timer
|
||||||
|
;; requests. Useful when running nested VMs: essentially extends timer
|
||||||
|
;; support up the branches of the VM tree toward the leaves.
|
||||||
(define (timer-relay [self-id 'timer-relay])
|
(define (timer-relay [self-id 'timer-relay])
|
||||||
(os-big-bang 'no-state
|
(os-big-bang 'no-state
|
||||||
(subscribe 'timer-relay
|
(subscribe 'timer-relay
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Trivial example program demonstrating os-udp.rkt working with os-big-bang.
|
||||||
|
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
(require "os-big-bang.rkt")
|
(require "os-big-bang.rkt")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Trivial example program demonstrating os-udp.rkt working with os-userland.
|
||||||
|
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
(require "os-userland-stdlib.rkt")
|
(require "os-userland-stdlib.rkt")
|
||||||
|
|
18
os-udp.rkt
18
os-udp.rkt
|
@ -1,5 +1,4 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
;; UDP drivers for os.rkt
|
;; UDP drivers for os.rkt
|
||||||
|
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
|
@ -25,6 +24,8 @@
|
||||||
;; TODO: BUG?: Routing packets between two local sockets won't work
|
;; TODO: BUG?: Routing packets between two local sockets won't work
|
||||||
;; because the patterns aren't set up to recognise that situation.
|
;; because the patterns aren't set up to recognise that situation.
|
||||||
|
|
||||||
|
;; BootK
|
||||||
|
;; Process acting as a UDP socket factory.
|
||||||
(define udp-driver
|
(define udp-driver
|
||||||
(userland
|
(userland
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
@ -41,6 +42,9 @@
|
||||||
(spawn (userland (udp-closer sname s)))
|
(spawn (userland (udp-closer sname s)))
|
||||||
sname]))))
|
sname]))))
|
||||||
|
|
||||||
|
;; UdpAddress UdpSocket -> -> Void
|
||||||
|
;; Relays this-level UDP messages "originating" at the given sname
|
||||||
|
;; down to real Racket UDP send I/O actions on the given socket.
|
||||||
(define ((udp-sender sname s))
|
(define ((udp-sender sname s))
|
||||||
(let loop ()
|
(let loop ()
|
||||||
(wait (message-handlers
|
(wait (message-handlers
|
||||||
|
@ -50,6 +54,10 @@
|
||||||
(meta-send (lambda () (udp-send-to s host port body)))
|
(meta-send (lambda () (udp-send-to s host port body)))
|
||||||
(loop)]))))
|
(loop)]))))
|
||||||
|
|
||||||
|
;; UdpAddress UdpSocket Exact -> -> Void
|
||||||
|
;; Relays meta-level UDP messages arriving at the given socket up to
|
||||||
|
;; this-level UdpPacket messages with sink equal to the given
|
||||||
|
;; sname. Received packets are limited to the given buffer-size.
|
||||||
(define ((udp-receiver sname s buffer-size))
|
(define ((udp-receiver sname s buffer-size))
|
||||||
(define buffer (make-bytes buffer-size))
|
(define buffer (make-bytes buffer-size))
|
||||||
(let loop ()
|
(let loop ()
|
||||||
|
@ -64,11 +72,19 @@
|
||||||
(send (udp-packet (udp-address host port) sname packet))
|
(send (udp-packet (udp-address host port) sname packet))
|
||||||
(loop)]))))
|
(loop)]))))
|
||||||
|
|
||||||
|
;; UdpAddress UdpSocket -> -> Void
|
||||||
|
;; Waits for a (list 'close sname) message. When it gets one, closes
|
||||||
|
;; the socket. Note that the other socket-specific driver processes
|
||||||
|
;; are also listening for close messages of this form.
|
||||||
(define ((udp-closer sname s))
|
(define ((udp-closer sname s))
|
||||||
(wait (message-handlers
|
(wait (message-handlers
|
||||||
[`(close ,(== sname))
|
[`(close ,(== sname))
|
||||||
(udp-close s)])))
|
(udp-close s)])))
|
||||||
|
|
||||||
|
;; BootK
|
||||||
|
;; Debugging aide: produces pretty hex dumps of UDP packets sent on
|
||||||
|
;; this network. Also prints out other messages without special
|
||||||
|
;; formatting.
|
||||||
(define udp-spy
|
(define udp-spy
|
||||||
(userland
|
(userland
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
;; Trivial example of os-userland.
|
||||||
|
|
||||||
(require "os-userland-stdlib.rkt")
|
(require "os-userland-stdlib.rkt")
|
||||||
|
|
||||||
|
|
152
os.rkt
152
os.rkt
|
@ -1,5 +1,4 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
;; Virtualized operating system.
|
;; Virtualized operating system.
|
||||||
|
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
|
@ -28,12 +27,15 @@
|
||||||
|
|
||||||
;; Each VM hosts 0 or more *multiplexed* processes. Each process has
|
;; Each VM hosts 0 or more *multiplexed* processes. Each process has
|
||||||
;; its own state record. In between schedulings, a process consists of
|
;; its own state record. In between schedulings, a process consists of
|
||||||
;; 1 or more message handlers. A message handler is a pair of a message
|
;; its state and (effectively) a multicontinuation. The
|
||||||
;; recogniser and a procedure taking a message and a process state to a
|
;; multicontinuation is implemented as a collection of message
|
||||||
;; new process state.
|
;; handlers, each of which is a pair of a message recogniser and a
|
||||||
|
;; procedure taking a message and a process state to a new process
|
||||||
|
;; state.
|
||||||
;;
|
;;
|
||||||
;; Each VM provides a *communication bus* for its processes to
|
;; Each VM provides a *communication bus* for its processes to
|
||||||
;; use. The communication bus is the only form of IPC the VM provides.
|
;; use. The communication bus is the only form of IPC the VM
|
||||||
|
;; provides. The bus provides a pub/sub-like routing facility.
|
||||||
;;
|
;;
|
||||||
;; Some processes *relay* messages out from the VM to other
|
;; Some processes *relay* messages out from the VM to other
|
||||||
;; VMs. Because the "tree" of VMs so formed has to be a tree - See
|
;; VMs. Because the "tree" of VMs so formed has to be a tree - See
|
||||||
|
@ -43,23 +45,17 @@
|
||||||
;; in effect, device-drivers, providing application-specific
|
;; in effect, device-drivers, providing application-specific
|
||||||
;; communication services to other processes in the VM.
|
;; communication services to other processes in the VM.
|
||||||
;;
|
;;
|
||||||
;; We split processes into "user" processes, permitted only to spawn
|
;; We may (but currently do not) split processes into "user"
|
||||||
;; other user processes and send messages on the VM's bus, and
|
;; processes, permitted only to spawn other user processes and send
|
||||||
;; "kernel" processes, permitted also to spawn other kernel processes
|
;; messages on the VM's bus, and "kernel" processes, permitted also to
|
||||||
;; and send messages to the VM's container.
|
;; spawn other kernel processes and send messages to the VM's
|
||||||
|
;; container.
|
||||||
;;
|
;;
|
||||||
;; Time plays an interesting role in a distributed system: if the
|
;; Time plays an interesting role in a distributed system: if the
|
||||||
;; medium messages are sent through isn't cooperative enough to let us
|
;; medium messages are sent through isn't cooperative enough to let us
|
||||||
;; know of a failed conversational participant, our only recourse is
|
;; know of a failed conversational participant, our only recourse is
|
||||||
;; /timeout/. Therefore, we require every level of the machine to
|
;; /timeout/. Since we have yet to address questions of failure here,
|
||||||
;; support timeouts, though we do not require such timeouts to be tied
|
;; we treat timeout events as we do any other I/O facility.
|
||||||
;; to real, wall-clock time: simulated time is just fine. This helps
|
|
||||||
;; with testability.
|
|
||||||
;;
|
|
||||||
;; Racket's alarm-evt is almost the right design for timeouts: its
|
|
||||||
;; synchronisation value should be the (or some) value of the clock
|
|
||||||
;; after the asked-for time. That way it serves as timeout and
|
|
||||||
;; clock-reader in one.
|
|
||||||
|
|
||||||
;; VMs are parameterised over:
|
;; VMs are parameterised over:
|
||||||
;; - the type of messages carried on the bus, Message
|
;; - the type of messages carried on the bus, Message
|
||||||
|
@ -67,13 +63,20 @@
|
||||||
;; - the type of messages to other VMs, MetaMessage
|
;; - the type of messages to other VMs, MetaMessage
|
||||||
;; - the type of patterns over MetaMessages, MetaMessagePattern
|
;; - the type of patterns over MetaMessages, MetaMessagePattern
|
||||||
|
|
||||||
|
;; VMs also come with algorithms that run MessagePatterns against
|
||||||
|
;; Messages (and MetaMessagePatterns against MetaMessages) in order to
|
||||||
|
;; decide whether they match or not:
|
||||||
|
;;
|
||||||
;; A PatternPredicate is a (MessagePattern Message -> Boolean), used
|
;; A PatternPredicate is a (MessagePattern Message -> Boolean), used
|
||||||
;; to match a message against a pattern.
|
;; to match a message against a pattern. A MetaPatternPredicate is
|
||||||
|
;; similar, but for metamessages.
|
||||||
|
|
||||||
;; A VM is a (vm ListBagOf<Suspension>
|
;; A VM is a (vm ListBagOf<Suspension>
|
||||||
;; QueueOf<Message> ;; TODO: make unordered?
|
;; ListOf<Message> ;; TODO: make unordered?
|
||||||
;; QueueOf<MetaMessage> ;; TODO: make unordered?
|
;; ListOf<MetaMessage> ;; TODO: make unordered?
|
||||||
;; QueueOf<BootK>).
|
;; ListOf<BootK>
|
||||||
|
;; PatternPredicate
|
||||||
|
;; MetaPatternPredicate).
|
||||||
(struct vm (suspensions
|
(struct vm (suspensions
|
||||||
pending-messages
|
pending-messages
|
||||||
pending-meta-messages
|
pending-meta-messages
|
||||||
|
@ -106,13 +109,7 @@
|
||||||
message-handlers
|
message-handlers
|
||||||
meta-message-handlers) #:transparent)
|
meta-message-handlers) #:transparent)
|
||||||
|
|
||||||
;; A HID is a per-VM unique value, used to identify specific
|
;; A MessageHandler is a (message-handler MessagePattern TrapK<Message>)
|
||||||
;; MetaMessageHandlers. Here, we use gensyms, though an alternative
|
|
||||||
;; (and purer) approach would be to keep a counter in the VM and use
|
|
||||||
;; that to construct IDs.
|
|
||||||
|
|
||||||
;; A MessageHandler is one of
|
|
||||||
;; -- (message-handler MessagePattern TrapK<Message>)
|
|
||||||
(struct message-handler (pattern k) #:transparent)
|
(struct message-handler (pattern k) #:transparent)
|
||||||
|
|
||||||
;; A KernelModeTransition is a
|
;; A KernelModeTransition is a
|
||||||
|
@ -131,13 +128,11 @@
|
||||||
;; A ListBagOf<X> is a ListOf<X> with the additional constraint that
|
;; A ListBagOf<X> is a ListOf<X> with the additional constraint that
|
||||||
;; order isn't meaningful.
|
;; order isn't meaningful.
|
||||||
|
|
||||||
;; TODO: is timeout really primitive? If so, isn't presence primitive?
|
;; BootK [#:pattern-predicate PatternPredicate] [#:meta-pattern-predicate MetaPatternPredicate]
|
||||||
;; TODO: what about metatimeout?
|
;; -> VM
|
||||||
;; TODO: what about spawn-meta-process etc? Come back to this later.
|
;; Constructs a generic VM layer. Optional arguments override the
|
||||||
;; TODO: enforce user-mode restrictions
|
;; default behaviour of using default-pattern-predicate for the two
|
||||||
;; TODO: timeouts
|
;; VM-specific recognisers.
|
||||||
|
|
||||||
;; BootK -> VM
|
|
||||||
(define (make-vm boot
|
(define (make-vm boot
|
||||||
#:pattern-predicate [pattern-predicate default-pattern-predicate]
|
#:pattern-predicate [pattern-predicate default-pattern-predicate]
|
||||||
#:meta-pattern-predicate [meta-pattern-predicate default-pattern-predicate])
|
#:meta-pattern-predicate [meta-pattern-predicate default-pattern-predicate])
|
||||||
|
@ -149,7 +144,10 @@
|
||||||
meta-pattern-predicate))
|
meta-pattern-predicate))
|
||||||
|
|
||||||
;; VM -> KernelModeTransition
|
;; VM -> KernelModeTransition
|
||||||
;; (A kind of Meta-InterruptK)
|
;; (A kind of Meta-InterruptK.) Performs a once-around of the nested
|
||||||
|
;; processes held in this VM, dispatching events to them and
|
||||||
|
;; collecting events from them. Returns a KernelModeTransition that
|
||||||
|
;; lets this VM communicate with its container.
|
||||||
(define (run-vm state)
|
(define (run-vm state)
|
||||||
(let* ((state (requeue-pollers state))
|
(let* ((state (requeue-pollers state))
|
||||||
(state (run-runnables state))
|
(state (run-runnables state))
|
||||||
|
@ -163,6 +161,9 @@
|
||||||
'()
|
'()
|
||||||
'())))
|
'())))
|
||||||
|
|
||||||
|
;; VM -> VM
|
||||||
|
;; If any suspended processes were simply yielding to let others run
|
||||||
|
;; and to let outside events enter, put them back on the runlist.
|
||||||
(define (requeue-pollers state)
|
(define (requeue-pollers state)
|
||||||
(foldl (lambda (susp state)
|
(foldl (lambda (susp state)
|
||||||
(if (suspension-polling? susp)
|
(if (suspension-polling? susp)
|
||||||
|
@ -171,21 +172,39 @@
|
||||||
(struct-copy vm state [suspensions '()])
|
(struct-copy vm state [suspensions '()])
|
||||||
(vm-suspensions state)))
|
(vm-suspensions state)))
|
||||||
|
|
||||||
|
;; VM -> VM
|
||||||
|
;; Run each runnable process on the runlist, incorporating the
|
||||||
|
;; information from each of their KernelModeTransitions into our state
|
||||||
|
;; for the next go-around.
|
||||||
(define (run-runnables state)
|
(define (run-runnables state)
|
||||||
(foldl (lambda (r state) (perform-transition (r) state))
|
(foldl (lambda (r state) (perform-transition (r) state))
|
||||||
(struct-copy vm state [pending-processes (list)])
|
(struct-copy vm state [pending-processes (list)])
|
||||||
(reverse (vm-pending-processes state))))
|
(reverse (vm-pending-processes state))))
|
||||||
|
|
||||||
|
;; VM -> VM
|
||||||
|
;; Dispatch each queued-up message across our internal pub/sub network
|
||||||
|
;; to all listening parties. See match-suspension for semantics of
|
||||||
|
;; routing and delivery.
|
||||||
(define (dispatch-messages state)
|
(define (dispatch-messages state)
|
||||||
(foldl dispatch-message
|
(foldl dispatch-message
|
||||||
(struct-copy vm state [pending-messages (list)])
|
(struct-copy vm state [pending-messages (list)])
|
||||||
(reverse (vm-pending-messages state))))
|
(reverse (vm-pending-messages state))))
|
||||||
|
|
||||||
|
;; Suspension -> ListOf<MetaMessageHandler>
|
||||||
|
;; Performs part of the level-shifting between a given VM and its
|
||||||
|
;; container: extracts all the meta-message-handlers from a given
|
||||||
|
;; suspension, converts them into *message* handlers for the VM below,
|
||||||
|
;; and returns them in a list.
|
||||||
(define (extract-downward-meta-message-handlers susp)
|
(define (extract-downward-meta-message-handlers susp)
|
||||||
(for/list ([mmh (suspension-meta-message-handlers susp)])
|
(for/list ([mmh (suspension-meta-message-handlers susp)])
|
||||||
(message-handler (message-handler-pattern mmh) (dispatch-meta-message mmh))))
|
(message-handler (message-handler-pattern mmh) dispatch-meta-message)))
|
||||||
|
|
||||||
(define (((dispatch-meta-message mmh) message) state)
|
;; MetaMessage -> VM -> KernelModeTransitions
|
||||||
|
;; (Which is to say, TrapK<MetaMessage> for VMs.) Handler invoked when
|
||||||
|
;; this VM's containing VM routes a message (at that level) to
|
||||||
|
;; us. Here we convert it to a *meta-message* and deliver it to
|
||||||
|
;; interested parties.
|
||||||
|
(define ((dispatch-meta-message message) state)
|
||||||
(run-vm
|
(run-vm
|
||||||
(foldl (match-suspension message
|
(foldl (match-suspension message
|
||||||
(vm-meta-pattern-predicate state)
|
(vm-meta-pattern-predicate state)
|
||||||
|
@ -194,6 +213,8 @@
|
||||||
(vm-suspensions state))))
|
(vm-suspensions state))))
|
||||||
|
|
||||||
;; KernelModeTransition VM -> VM
|
;; KernelModeTransition VM -> VM
|
||||||
|
;; Extracts the subscriptions and actions from the given transition
|
||||||
|
;; and incorporates them into state.
|
||||||
(define (perform-transition transition state)
|
(define (perform-transition transition state)
|
||||||
(match transition
|
(match transition
|
||||||
[(kernel-mode-transition new-suspension
|
[(kernel-mode-transition new-suspension
|
||||||
|
@ -208,12 +229,23 @@
|
||||||
[other
|
[other
|
||||||
(error 'vm "Processes must return a kernel-mode-transition struct; got ~v" other)]))
|
(error 'vm "Processes must return a kernel-mode-transition struct; got ~v" other)]))
|
||||||
|
|
||||||
|
;; Message VM -> VM
|
||||||
|
;; Enqueues a message for later delivery over this VM's pub/sub bus.
|
||||||
(define (enqueue-message message state)
|
(define (enqueue-message message state)
|
||||||
(struct-copy vm state [pending-messages (cons message (vm-pending-messages state))]))
|
(struct-copy vm state [pending-messages (cons message (vm-pending-messages state))]))
|
||||||
|
|
||||||
|
;; BootK VM -> VM
|
||||||
|
;; Places a runnable process on the runlist.
|
||||||
(define (enqueue-runnable r state)
|
(define (enqueue-runnable r state)
|
||||||
(struct-copy vm state [pending-processes (cons r (vm-pending-processes state))]))
|
(struct-copy vm state [pending-processes (cons r (vm-pending-processes state))]))
|
||||||
|
|
||||||
|
;; Suspension VM -> VM
|
||||||
|
;; If the suspension is provably inert, discard it; otherwise, add it
|
||||||
|
;; to the collection of suspended processes in state. We currently
|
||||||
|
;; only have a conservative means of showing that a process is inert:
|
||||||
|
;; when it has no immediately-ready continuation, no message
|
||||||
|
;; subscriptions and no meta-message subscriptions, it is considered
|
||||||
|
;; inert here.
|
||||||
(define (enqueue-suspension susp state)
|
(define (enqueue-suspension susp state)
|
||||||
(match susp
|
(match susp
|
||||||
[(suspension _ #f '() '())
|
[(suspension _ #f '() '())
|
||||||
|
@ -222,9 +254,15 @@
|
||||||
[(suspension _ _ _ _)
|
[(suspension _ _ _ _)
|
||||||
(struct-copy vm state [suspensions (cons susp (vm-suspensions state))])]))
|
(struct-copy vm state [suspensions (cons susp (vm-suspensions state))])]))
|
||||||
|
|
||||||
|
;; MetaMessage VM -> VM
|
||||||
|
;; Enqueues a metamessage for later delivery over this VM's pub/sub bus.
|
||||||
(define (enqueue-meta-message message state)
|
(define (enqueue-meta-message message state)
|
||||||
(struct-copy vm state [pending-meta-messages (cons message (vm-pending-meta-messages state))]))
|
(struct-copy vm state [pending-meta-messages (cons message (vm-pending-meta-messages state))]))
|
||||||
|
|
||||||
|
;; Message VM -> VM
|
||||||
|
;; Routes a single Message to interested suspended processes, resuming
|
||||||
|
;; them as necessary. See match-suspension for semantics of routing
|
||||||
|
;; and delivery.
|
||||||
(define (dispatch-message message state)
|
(define (dispatch-message message state)
|
||||||
(foldl (match-suspension message
|
(foldl (match-suspension message
|
||||||
(vm-pattern-predicate state)
|
(vm-pattern-predicate state)
|
||||||
|
@ -232,6 +270,14 @@
|
||||||
(struct-copy vm state [suspensions '()])
|
(struct-copy vm state [suspensions '()])
|
||||||
(vm-suspensions state)))
|
(vm-suspensions state)))
|
||||||
|
|
||||||
|
;; Message PatternPredicate (Suspension -> ListOf<MessageHandler>)
|
||||||
|
;; -> Suspension VM -> VM
|
||||||
|
;; Curried function. Uses handlers-getter to select either the
|
||||||
|
;; message-handlers or meta-message-handlers of susp, and walks
|
||||||
|
;; through them one at a time. If one matches the message, the message
|
||||||
|
;; is delivered to the process and none of the other handlers are
|
||||||
|
;; tried. Otherwise, if we end up with no handlers having matched, the
|
||||||
|
;; suspension is re-enqueued on the suspended process list of state.
|
||||||
(define ((match-suspension message apply-pattern handlers-getter) susp state)
|
(define ((match-suspension message apply-pattern handlers-getter) susp state)
|
||||||
(let search-handlers ((message-handlers (handlers-getter susp)))
|
(let search-handlers ((message-handlers (handlers-getter susp)))
|
||||||
(cond
|
(cond
|
||||||
|
@ -246,6 +292,8 @@
|
||||||
[else
|
[else
|
||||||
(search-handlers (cdr message-handlers))])))
|
(search-handlers (cdr message-handlers))])))
|
||||||
|
|
||||||
|
;; Suspension -> Boolean
|
||||||
|
;; True iff the suspension can be immediately resumed without an external event.
|
||||||
(define (suspension-polling? susp)
|
(define (suspension-polling? susp)
|
||||||
(not (eq? (suspension-k susp) #f)))
|
(not (eq? (suspension-k susp) #f)))
|
||||||
|
|
||||||
|
@ -257,6 +305,12 @@
|
||||||
(not (null? (vm-pending-messages state)))
|
(not (null? (vm-pending-messages state)))
|
||||||
(ormap suspension-polling? (vm-suspensions state))))
|
(ormap suspension-polling? (vm-suspensions state))))
|
||||||
|
|
||||||
|
;; BootK [#:pattern-predicate PatternPredicate] [#:meta-pattern-predicate MetaPatternPredicate]
|
||||||
|
;; -> BootK
|
||||||
|
;; Constructs a VM that will start with the passed-in BootK. Returns a
|
||||||
|
;; BootK representing the new VM, that is suitable for spawning as a
|
||||||
|
;; process in some containing VM. This is the glue between adjacent
|
||||||
|
;; layers in the tower.
|
||||||
(define (nested-vm boot
|
(define (nested-vm boot
|
||||||
#:pattern-predicate [pattern-predicate default-pattern-predicate]
|
#:pattern-predicate [pattern-predicate default-pattern-predicate]
|
||||||
#:meta-pattern-predicate [meta-pattern-predicate default-pattern-predicate])
|
#:meta-pattern-predicate [meta-pattern-predicate default-pattern-predicate])
|
||||||
|
@ -264,13 +318,18 @@
|
||||||
#:pattern-predicate pattern-predicate
|
#:pattern-predicate pattern-predicate
|
||||||
#:meta-pattern-predicate meta-pattern-predicate))))
|
#:meta-pattern-predicate meta-pattern-predicate))))
|
||||||
|
|
||||||
|
;; PatternPredicate (and also MetaPatternPredicate). The default
|
||||||
|
;; implementation: expects a MessagePattern (or MetaMessagePattern) to
|
||||||
|
;; be a predicate on a Message (or a MetaMessage).
|
||||||
(define default-pattern-predicate
|
(define default-pattern-predicate
|
||||||
(lambda (p m) (p m)))
|
(lambda (p m) (p m)))
|
||||||
|
|
||||||
;;---------------------------------------------------------------------------
|
;;---------------------------------------------------------------------------
|
||||||
|
|
||||||
(define (nested-vm-inert? sub)
|
;; Suspension -> Boolean
|
||||||
(match sub
|
;; True iff provably inert. Uses a conservative definition of inertness.
|
||||||
|
(define (nested-vm-inert? susp)
|
||||||
|
(match susp
|
||||||
[(suspension (vm _ '() '() '() _ _) #f '() '())
|
[(suspension (vm _ '() '() '() _ _) #f '() '())
|
||||||
;; Inert iff not waiting for any messages or metamessages, and
|
;; Inert iff not waiting for any messages or metamessages, and
|
||||||
;; with no internal work left to do.
|
;; with no internal work left to do.
|
||||||
|
@ -283,12 +342,19 @@
|
||||||
(define (match-ground-event p m)
|
(define (match-ground-event p m)
|
||||||
(equal? (ground-event-pattern-tag p) (ground-event-value-tag m)))
|
(equal? (ground-event-pattern-tag p) (ground-event-value-tag m)))
|
||||||
|
|
||||||
;; PatternPredicate ( -> KernelModeTransition ) -> Void
|
;; BootK [#:pattern-predicate PatternPredicate] -> Void
|
||||||
|
;;
|
||||||
|
;; Starts running (the lowest level of) a tower of VMs. This lowest
|
||||||
|
;; level has special support for routing metaevents to and from the
|
||||||
|
;; Racket event-handling and I/O mechanisms.
|
||||||
|
;;
|
||||||
;; In this context,
|
;; In this context,
|
||||||
;; Message = a thunk
|
;; Message = a thunk
|
||||||
;; MessagePattern = evt?
|
;; MessagePattern = evt?
|
||||||
;; MetaMessage, MetaMessagePattern = not defined because there's no outer level
|
;; MetaMessage, MetaMessagePattern = not defined because there's no outer level
|
||||||
;; Runs its argument VM until it becomes (provably) inert.
|
;;
|
||||||
|
;; Runs a VM booted with the given BootK until the VM becomes
|
||||||
|
;; (provably) inert.
|
||||||
(define (ground-vm boot
|
(define (ground-vm boot
|
||||||
#:pattern-predicate [pattern-predicate default-pattern-predicate])
|
#:pattern-predicate [pattern-predicate default-pattern-predicate])
|
||||||
(let loop ((transition (run-vm (make-vm boot
|
(let loop ((transition (run-vm (make-vm boot
|
||||||
|
|
Loading…
Reference in New Issue