Part IV: Runtime System
Version: 1.0 Draft Last Updated: 2025-12-20
1. Introduction
The runtime system executes the graph constructed during wiring. It manages:
Object lifecycle (initialization, start, stop, disposal)
Time progression and scheduling
Evaluation ordering and execution
Event propagation and notification
graph TB
subgraph "Runtime Components"
G[Graph]
SCH[Scheduler]
CLK[Clock]
ENG[Engine]
N1[Node 1]
N2[Node 2]
N3[Node 3]
end
ENG --> SCH
ENG --> CLK
ENG --> G
G --> N1
G --> N2
G --> N3
SCH --> |"schedule"| N1
SCH --> |"schedule"| N2
CLK --> |"current time"| ENG
2. Lifecycle
All runtime components follow a consistent lifecycle:
2.1 State Machine
stateDiagram-v2
[*] --> Constructed
Constructed --> Initialized: initialise()
Initialized --> Started: start()
Started --> Evaluating: eval()
Evaluating --> Started: tick complete
Started --> Stopped: stop()
Stopped --> Started: start()
Stopped --> Disposed: dispose()
Disposed --> [*]
2.2 Lifecycle Methods
Method |
Description |
Called When |
|---|---|---|
|
One-time setup, allocate resources |
Before first start |
|
Begin active operation |
Each run/resume |
|
Perform computation |
Each scheduled tick |
|
Pause operation |
End run or pause |
|
Release all resources |
Final cleanup |
2.3 Lifecycle Guarantees
Guarantee |
Description |
|---|---|
Single Init |
|
Matched Start/Stop |
Every |
Order |
|
Topological |
Nodes initialized/started in dependency order |
Reverse Dispose |
Nodes disposed in reverse dependency order |
3. Graph
The Graph is the top-level runtime container:
3.1 Graph Structure
classDiagram
class Graph {
+parent_node: Node | None
+graph_id: str
+nodes: Sequence[Node]
+evaluation_clock: EvaluationClock
+engine_evaluation_clock: EngineEvaluationClock
+evaluation_engine: EvaluationEngine
+evaluation_time: datetime
+last_evaluation_time: datetime
+is_started: bool
+initialise()
+start()
+stop()
+dispose()
+schedule_node(node_ndx, when, force_set)
+evaluate_graph()
}
class Node {
+graph: Graph
+inputs: list[Input]
+output: Output
+eval()
}
class EvaluationClock {
+evaluation_time: datetime
+now: datetime
+cycle_time: timedelta
+next_cycle_evaluation_time: datetime
}
Graph "1" --> "*" Node
Graph "1" --> "1" EvaluationClock
3.2 Graph Properties
Property |
Type |
Description |
|---|---|---|
|
`Node |
None` |
|
|
Unique graph identifier |
|
|
All nodes in topological order |
|
|
Current tick’s time |
|
|
Time of last evaluation |
|
|
User-facing clock |
|
|
Engine clock (internal) |
|
|
Evaluation engine |
|
|
Engine API interface |
|
|
Graph configuration traits |
|
|
Push source event receiver |
|
|
Scheduled evaluation times |
|
|
True if graph is running |
4. Scheduler
The Scheduler manages when nodes are evaluated:
4.1 Scheduling Model
graph TB
subgraph "Scheduler"
PQ[Priority Queue<br/>time → nodes]
NOW[Current Time]
READY[Ready Queue]
end
N1[Node 1] --> |"schedule_node(t1)"| PQ
N2[Node 2] --> |"schedule_node(t2)"| PQ
N3[Node 3] --> |"notify()"| READY
PQ --> |"pop at time"| READY
READY --> |"evaluate"| ENG[Engine]
4.2 Scheduling Operations
Operation |
Description |
|---|---|
|
Add node to be evaluated at |
|
Get all nodes scheduled for |
|
Return next time with pending nodes |
|
True if any nodes are scheduled |
4.3 Priority Ordering
Within a single tick, nodes are ordered by:
Topological rank (sources before dependents)
Node ID (for deterministic ordering within same rank)
graph LR
subgraph "Tick at T=100"
direction LR
R1[Rank 0<br/>Sources]
R2[Rank 1<br/>Compute]
R3[Rank 2<br/>Compute]
R4[Rank 3<br/>Sinks]
end
R1 --> R2 --> R3 --> R4
5. Clock
The Clock provides time management:
5.1 Clock Types
graph TD
CLK[Clock]
CLK --> SIM[Simulation Clock]
CLK --> RT[Real-Time Clock]
SIM --> |"Advances as fast as possible"| FAST[Fast-forward]
RT --> |"Tracks wall clock"| WALL[Wall Clock]
5.2 Clock Properties
Property |
Description |
|---|---|
|
Current time |
|
Time being evaluated |
|
Source of async events |
|
Current evaluation cycle ID |
5.3 Time Semantics
Mode |
Time Behavior |
|---|---|
Simulation |
Jumps directly to next scheduled event |
Real-Time |
Sleeps until next scheduled event |
6. Evaluation Loop
6.1 Main Loop
sequenceDiagram
participant E as Engine
participant S as Scheduler
participant G as Graph
participant N as Nodes
E->>S: Initialize
loop Until end_time or stop
E->>S: next_scheduled_time()
S-->>E: next_time
alt Simulation Mode
E->>G: Set evaluation_time = next_time
else Real-Time Mode
E->>E: Sleep until next_time
E->>G: Set evaluation_time = now()
end
E->>S: pop_scheduled_nodes()
S-->>E: nodes_to_evaluate
loop For each node in topological order
E->>N: eval()
N->>N: Read inputs
N->>N: Compute
N->>N: Write output
N-->>E: May schedule more nodes
end
E->>E: Increment cycle
end
6.2 Evaluation Phases Per Tick
Phase |
Description |
|---|---|
1. Collect |
Get all nodes scheduled for current time |
2. Sort |
Order by topological rank |
3. Evaluate |
Call |
4. Propagate |
Handle notifications and scheduling |
5. Cleanup |
Reset per-tick state |
6.3 Node Evaluation
sequenceDiagram
participant E as Engine
participant N as Node
participant I as Inputs
participant O as Output
E->>N: eval()
N->>I: Check modified inputs
alt Any active input modified
N->>I: Read values
N->>N: Execute function
alt Has output
N->>O: Write result
O->>O: Mark modified
O->>E: Notify dependents
end
end
7. Notification System
7.1 Notification Flow
graph TB
O[Output Modified]
O --> N1[Notify Bound Inputs]
N1 --> N2[Check Active Status]
N2 --> |"Active"| S[Schedule Node]
N2 --> |"Passive"| SKIP[Skip]
S --> Q[Add to Scheduler Queue]
7.2 Notification Idempotency
Notifications are deduplicated per evaluation time:
def notify(self, evaluation_time):
if self._notify_time != evaluation_time:
self._notify_time = evaluation_time
self._propagate_to_parent()
self._schedule_node()
7.3 Parent Chain Notification
For nested time-series (bundles, collections):
graph TB
C[Child TS Modified]
C --> P1[Parent TSB Field]
P1 --> P2[Parent TSB]
P2 --> N[Owning Node Input]
N --> S[Scheduler]
8. Input/Output Binding
8.1 Binding Model
graph LR
subgraph "Node A"
OA[Output]
end
subgraph "Node B"
IB[Input]
end
subgraph "Node C"
IC[Input]
end
OA --> |"bind"| IB
OA --> |"bind"| IC
8.2 Binding Operations
Operation |
Description |
|---|---|
|
Connect input to output |
|
Disconnect from output |
|
True if connected to an output |
8.3 Binding Effects
When binding occurs:
Input marks itself as bound
Input subscribes to output notifications
If output is valid, input copies current value
If input is active, node is scheduled
9. Active/Passive Subscriptions
9.1 Subscription Model
State |
Behavior |
|---|---|
Active |
Input triggers node evaluation on modification |
Passive |
Input receives updates but doesn’t trigger evaluation |
9.2 State Transitions
stateDiagram-v2
[*] --> Active: default
Active --> Passive: make_passive()
Passive --> Active: make_active()
9.3 Subscription Use Cases
@compute_node
def sample(trigger: TS[bool], value: TS[int]) -> TS[int]:
"""Only emit when trigger fires, using current value."""
# 'value' could be passive - only read when trigger fires
return value.value
10. Modification Tracking
10.1 Tracking Invariant
modified = (last_modified_time == evaluation_time)
A time-series is modified if it was changed in the current tick.
10.2 Tracking Implementation
Property |
Description |
|---|---|
|
Time of most recent modification |
|
Computed: |
10.3 Modification Propagation
sequenceDiagram
participant V as Value
participant O as Output
participant I as Inputs
participant N as Nodes
V->>O: set_value()
O->>O: Update last_modified_time
O->>O: Mark modified
O->>I: Notify bound inputs
I->>N: Schedule if active
11. Error Handling
11.1 Error Types
Error Type |
Description |
|---|---|
|
General graph errors |
|
Node-specific errors |
|
Scheduling failures |
|
Runtime evaluation failures |
11.2 Error Propagation
graph TB
N[Node Error]
N --> EO[Error Output]
EO --> EH[Error Handler Node]
EH --> |"recover"| CONT[Continue]
EH --> |"fail"| STOP[Stop Graph]
11.3 Error Outputs
Nodes can have optional error outputs:
@compute_node
def risky_operation(x: TS[int]) -> TSB[ValueWithError]:
try:
result = complex_calculation(x.value)
return {"value": result, "error": None}
except Exception as e:
return {"value": None, "error": str(e)}
12. Execution Modes
12.1 Simulation Mode
graph LR
T1[t=0] --> |"instant"| T2[t=100]
T2 --> |"instant"| T3[t=500]
T3 --> |"instant"| T4[t=1000]
Characteristics:
Time jumps to next scheduled event
No push sources allowed
Deterministic execution
Reproducible results
12.2 Real-Time Mode
graph LR
T1[t=0] --> |"sleep 100ms"| T2[t=100]
T2 --> |"sleep 400ms"| T3[t=500]
T3 --> |"sleep 500ms"| T4[t=1000]
Characteristics:
Time tracks wall clock
Push sources allowed
Non-deterministic (external events)
May process late events
12.3 Mode Comparison
Aspect |
Simulation |
Real-Time |
|---|---|---|
Time progression |
As fast as possible |
Wall clock |
Push sources |
Prohibited |
Allowed |
Determinism |
Guaranteed |
Not guaranteed |
Reproducibility |
Full |
Limited |
Use case |
Backtesting |
Live operation |
13. Injectable Types
13.1 Available Injectables
Injectable |
Description |
|---|---|
|
Mutable state container |
|
Node scheduling access |
|
Time information |
|
State with replay support |
|
Direct output access |
|
Runtime context |
13.2 Usage
@compute_node
def stateful_counter(
trigger: TS[bool],
state: STATE[int] = 0
) -> TS[int]:
if trigger.value:
state.value += 1
return state.value
13.3 Injection Lifecycle
Injectable |
When Provided |
|---|---|
|
Created at initialization |
|
Available from start |
|
Available always |
14. Engine Configuration
14.1 Configuration Options
Option |
Description |
|---|---|
|
Evaluation start time |
|
Evaluation end time (optional) |
|
SIMULATION or REAL_TIME |
|
Enable execution tracing |
14.2 run_graph Function
def run_graph(
graph: GraphBuilder,
start_time: datetime = MIN_DT,
end_time: datetime | None = None,
run_mode: EvaluationMode = SIMULATION,
**kwargs
) -> dict[str, Any] | None:
"""Execute a graph."""
15. Thread Safety
15.1 Threading Model
Component |
Thread Safety |
|---|---|
Scheduler |
Single-threaded access |
Evaluation |
Single evaluation thread |
Push sources |
Multi-threaded injection |
Internal state |
Protected by evaluation model |
15.2 Push Source Threading
graph TB
subgraph "External Threads"
T1[Thread 1]
T2[Thread 2]
end
subgraph "Engine"
Q[Event Queue]
EV[Evaluation Thread]
end
T1 --> |"push"| Q
T2 --> |"push"| Q
Q --> |"drain"| EV
16. Implementation Notes
16.1 Push Source Node Processing Phase
Push source nodes have special handling in the evaluation loop that occurs BEFORE normal compute nodes:
sequenceDiagram
participant EQ as Event Queue
participant EN as Engine
participant PN as Push Nodes
participant CN as Compute Nodes
Note over EQ,CN: Evaluation Tick Start
EQ->>EN: Dequeue push messages
EN->>PN: Apply messages to push nodes
Note over PN: push_source_nodes_end boundary
EN->>CN: Evaluate compute nodes
Note over EQ,CN: Evaluation Tick End
If message application fails, messages are re-queued with backpressure signaling.
16.2 Field-Level Modification Tracking (TSB)
Time-series bundles support field-level modification tracking:
# When a bundle field is modified:
field_output.set(new_value)
# This triggers parent chain notification:
self._parent_or_node.mark_child_modified(self, modified_time)
This enables:
Downstream node scheduling when individual TSB fields change (not just the bundle)
Efficient delta tracking for nested structures
Parent chain notification for TSD and nested bundles
16.3 Node Evaluation Guard Conditions
Nodes evaluate only when ALL of the following are satisfied:
Condition |
Description |
|---|---|
Required inputs valid |
All non-optional inputs have valid values |
Collection completeness |
For |
Trigger present |
Either input modified OR scheduler triggered |
Scheduler verification |
For scheduler-using nodes, something actually triggered scheduling |
16.4 Node-Level Scheduling (NodeSchedulerImpl)
Each node can access its own scheduler via _scheduler: SCHEDULER injectable:
@compute_node
def my_node(ts: TS[int], _scheduler: SCHEDULER = None) -> TS[int]:
if ts.modified:
_scheduler.schedule(MIN_TD * 5, tag="delayed") # Schedule future tick
return ts.value
if _scheduler.is_scheduled_now:
return -1 # Handle scheduled tick
Features:
Sorted event list with optional tags
Alarm support with callbacks
is_scheduled_nowproperty for checking current tickWall clock scheduling for real-time mode
17. Reference Locations
Component |
Python Location |
C++ Location |
|---|---|---|
Graph |
|
|
Node |
|
|
Scheduler |
|
|
Evaluation Engine |
|
|
Clock |
|
|
17. Next Steps
Continue to:
05_TIME_SERIES_TYPES.md - Time-series type details
06_NODE_TYPES.md - Node specifications
07_OPERATORS.md - Built-in operators