aether
2021-12-09
A DSL for emulating an actor-based distributed system, housed on a family of emulated devices.
Upstream URL
Author
License
aether
aether
is a Common Lisp framework for emulating an actor-based distributed system housed on a family of emulated devices.
Overview
The purpose of aether
is to provide a testbed in which one can design the behaviors of distributed electronics, emulate software running on them, and instrument the emulation to infer software performance characteristics.
The framework is segmented into the following layers:
- Time-domain simulation via time-triggered events.
- Physical and logical network layers.
- An actor-based DSL.
- A library of standard patterns in distributed programming.
Each layer comes with standard instances which embody default behaviors. These can be used as-is for "macroscopic", detail-free emulation, and they can be subclassed to permit the injection of detailed, domain-specific behaviors (e.g., faithful emulation of network load). The actor-based DSL can be useful in the specification of both the behavior of the electronics and the behavior of the program: starting with the latter gives information about algorithm performance in the case of "ideal" hardware, and migrating to the latter pins down the performance features specific to that case.
Installation
The simplest installation method is to use Quicklisp: running (ql:quickload "aether")
will download aether
as well as any project dependencies.
Before aether
is available via the Quicklisp index, or to use a bleeding-edge version, clone the aether
git repository into your Quicklisp local project directory and then quickload as above.
Example
Here is an example description of a simple processor which responds to external impulses (i.e., messages on the aether
network) to compute integer factorials.
(defclass arithmetic-server (process) ((process-clock-rate :initform 1)) (:documentation "Simple server process that handles an arithmetic RPC call.")) ;;; processor description / computational primitives ;; all PROCESS instances begin with the command START (define-process-upkeep ((process arithmetic-server) now) (START) "Make the processor sit in an infinite \"listening\" loop." (process-continuation process `(START))) (define-process-upkeep ((process arithmetic-server) now) (PUSH n) (push n (process-data-stack process))) (define-process-upkeep ((process arithmetic-server) now) (MULTIPLY) (let ((left (pop (process-data-stack process))) (right (pop (process-data-stack process)))) (push (* left right) (process-data-stack process)))) (define-process-upkeep ((process arithmetic-server) now) (EMIT address) (let* ((result (pop (process-data-stack process))) (message (make-message-rpc-done :result result))) (send-message address message))) ;;; public-facing factorial service (defstruct (message-factorial (:include message)) (n nil :type (integer 0))) (define-message-handler handle-message-factorial ((process arithmetic-server) (message message-factorial) now) (process-continuation process `(FACTORIAL ,(message-factorial-n message)) `(EMIT ,(message-reply-channel message)))) (define-process-upkeep ((process arithmetic-server) now) (FACTORIAL n) (cond ((zerop n) (process-continuation process `(PUSH 1))) (t (process-continuation process `(FACTORIAL ,(1- n)) `(PUSH ,n) `(MULTIPLY))))) ;;; service registry (define-message-dispatch arithmetic-server (message-factorial 'handle-message-factorial)) ;;; example: spawning a server and querying it ;; set up the actors (let* ((simulation (make-simulation)) (*local-courier* (make-instance 'courier)) (server (spawn-process 'arithmetic-server)) (reply-channel (register))) (simulation-add-event simulation (make-event :callback server)) (simulation-add-event simulation (make-event :callback *local-courier*)) ;; send the factorial request (send-message (process-public-address server) (make-message-factorial :reply-channel reply-channel :n 15)) ;; wait a while for it to complete (simulation-run simulation :canary (canary-until 60)) ;; unpack the reply (receive-message (reply-channel done-message) (message-rpc-done (unregister reply-channel) (message-rpc-done-result done-message))))
Further examples can be found under the directory tests/examples/
in the source tree.
They primarily demonstrate features of the standard library.
These "living" examples are run regularly as part of the test suite.
License
aether
is made available under the MIT license.
See LICENSE.md
in the source tree for more information.
Reference
Eric C. Peterson and Peter J. Karalekas. 2021. aether
: Distributed system emulation in Common Lisp. In Proceedings of the 14th European Lisp Symposium (ELS’21). ACM, New York, NY, USA, 9 pages. https://doi.org/10.5281/zenodo.4713971