System Architecture - craterdog-archives/js-bali-virtual-machine GitHub Wiki
Musings
"The tiny ant, way down inside, can do great things when multiplied." —the craterdog
Overview
The Bali Virtual Machine™ consists of a set of processors that run (on demand) in the Bali Nebula™ and are used to process messages. When a new message is sent to a component residing in the Bali Nebula™ a new task is placed in the virtual machine's task bag to process the messsage. While the task bag still contains tasks, processor instances are created to handle each task. Each task is removed from the task bag and given to a processor for processing. When a task is waiting on an event, the task is returned to the bag to be processed later. When a processor has finished processing a task, or an unhandled exception was thrown by the task, the processor is given another task to process or is shutdown.
- A new task is created when the virtual machine removes a message from the message bag and spawns a new processor to process the message.
- The processor creates a new invocation context recording the message (including any arguments passed with it), the target component, and the initial processor state.
- The processor reads in the compiled type document for the component that is the target of the message.
- The processor then executes the machine instructions associated with the procedure with the same name as the message.
- During processing, multiple documents will likely be created and/or read from the document repository.
- Also, additional messages will likely be posted to the message bag for processing by a different processor.
- And finally, events will likely be published to the event bag to be retrieved by the virtual machine to notify all interested parties of the event by posting notification messages to the message bag.
Processor
Each processor operates on a single task at a time and a single execution context within that task.
A processor consists of the following elements.
- current task - the current task assigned to the processor for processing.
- current context - the current execution context within the task.
- instruction set - the set of eight types of instructions that can be executed by the processor.
- intrinsic functions - the set of intrinsic stack-based functions that are available to the processor.
The processor can be run in two different modes: continuous mode which attempts to execute the entire task; or single-step mode which pauses between each instruction to allow debugging.
The pseudo-code for continuous processing is defined as:
while task.isReady() do {
processor.step()
}
if task.isDone() {
publish [
$tag: tag()
$attributes: [
$task: task[$tag]
$account: task[$account]
$cost: task[$clock]
$target: task[$target]
$message: task[$message]
$parameters: task[$parameters]
$response: task[$response]
]
]($type: /nebula/events/TaskCompleted/v1)
} else {
post task to /nebula/bags/Tasks/v1
}
processor.reset()
post processor to /nebula/bags/Processors/v1
The pseudo-code for single-step processing is defined as:
if task.isReady() then {
processor.step()
return true
}
return false
Task
A task maintains the set of attributes that capture the current processing state for the task.
This state can be stored in a document and placed in a task bag when there is no processor currently processing it. A task consists of the following attributes.
- tag - the unique identifier for the task being processed.
- state - the current state of the processing (
$active
,$frozen
,$paused
,$completed
,$abandoned
). - account - the identifier for the account of the entity that requested the processing.
- tokens - the number of tokens that were allocated by the entity to pay for the processing of this task.
- clock - the number of instructions that have been executed for this task thus far.
- response - the response to the initial message that spawned this task.
- components - a stack containing the components being operated on by the instructions.
- contexts - a stack of invocation contexts for the messages being processed as part of this task.
Context
An invocation context maintains the attributes associated with the processing of a single message targeting a specific component.
The lighter colored boxes represent the static attributes and the darker colored boxes are dynamic attributes. An invocation context consists of the following attributes.
- target - the component that is the target of the sent message.
- message - the symbol representing the message that was sent.
- arguments - the list of components that were passed as arguments associated with the message.
- variables - the catalog of named variable values that were defined in the original source code or by the compiler as temporary variables.
- constants - the catalog of named constant values that were defined in the original source code.
- literals - the set of the literal values that were defined in the original source code.
- messages - the set of messages sent in the code being executed in this context.
- handlers - a stack of exception handler addresses that is traversed each time an exception is thrown.
- bytecode - the list of the two byte representation for each assembled instruction. The index into the list is the address of the instruction (e.g. 1 for first two bytes, 2 for the second two bytes, etc.).
- address - the address (index) of the next instruction to be executed.
- instruction - a two byte number representing the next instruction to be executed.
If, during the processing of one message another message is sent, a new invocation context will be created either within the same task or in a new task depending on whether the message is to be processed synchronously or asynchronously.