PLC runtime — architecture¶
Detailed-design notes for the soft-real-time PLC heart family (PLC runtime heart on iceoryx2 (FEAT_0010)). This page currently covers the bounded-time dispatch sub-feature (Bounded-time dispatch (FEAT_0017)) and its zero-allocation guarantee (No heap allocation in dispatch (REQ_0060)); other sub-features are added as their designs land.
Per the arc42 conventions used across this spec, design decisions are
captured as arch-decision directives, structural elements as
building-block directives, and concrete code mappings as impl
directives. Test cases live in PLC runtime — verification.
Solution strategy¶
The dispatch hot path’s zero-allocation goal is solved by moving every
per-iteration allocation up to ``Executor::build`` time and reusing
that capacity. Two design choices follow from that posture: how to
reuse the per-iteration error slot, and how to replace the unbounded
crossbeam re-dispatch channel that Graph::run_once allocates today.
Context. Today Decision. Provision all per-iteration scratch at
Alternatives considered.
Consequences. ✅ Steady-state dispatch performs zero heap allocations
(per No heap allocation in dispatch (REQ_0060)).
✅ Worst-case re-dispatch latency is bounded by ring capacity,
not allocator behaviour.
❌ Adds one |
Building blocks¶
The collection of fields hoisted from per-iteration locals onto
Lifetime contract: every field is created in |
Implementation¶
Concrete Rust changes that realise Dispatch scratch (pre-alloc... (BB_0023). In ``crates/taktora-executor/src/executor.rs``
In ``crates/taktora-executor/src/pool.rs``
In ``crates/taktora-executor/src/graph.rs``
In ``crates/taktora-executor/src/task_kind.rs``
New module ``crates/taktora-executor/src/ready_ring.rs``
Verification harness
|
Scan-cycle observability¶
Detailed design for the scan-cycle observability sub-feature
(Scan-cycle observability (FEAT_0021)). Two structural pieces: a fixed-bucket histogram
for percentile estimation (chosen for its allocation-free, bounded-time
per-sample update path), and per-task aggregate slots allocated at
Executor::build time.
Context. Per-task latency percentiles (REQ_0100) requires p50 / p95 / p99 execute-duration percentiles per task over a sliding window, and Allocation-free telemetry u... (REQ_0104) requires the update path to be allocation-free with bounded per-sample latency. A window-of-raw-samples approach (keep the last N samples, sort on query) is allocation-free if N is fixed at build time but pays O(N log N) on every query. Streaming sketches (t-digest, CKMS) give tight p99 accuracy but their compaction step is amortised, not bounded, and they reshape memory as data arrives. Decision. Use a fixed-bucket log-linear histogram covering the
value range 100 ns … 10 s with at least three buckets per decade
(eight decades × three buckets ≈ 24 active buckets, padded to a
power of two for cheap indexing). The bucket layout is fixed at
compile time as a Alternatives considered.
Consequences. ✅ Per-sample update is O(1) and allocation-free (per Allocation-free telemetry u... (REQ_0104)). ✅ Per-task memory footprint is bounded and known at build time (~1 kB / task for the histogram + snapshots). ❌ Percentile values are bucket-quantised — relative accuracy is bounded by bucket width (~33% within a single bucket, ≤ 1% at the bucket centroid). Acceptable for soft-RT telemetry; the Cyclictest-style benchmark ... (REQ_0111) harness exposes raw samples for finer offline analysis when needed. |
One |
|
Concrete Rust changes that realise Per-task cycle statistics (BB_0050) and Statistics snapshot view (BB_0051). New module ``crates/taktora-executor/src/stats/``
In ``crates/taktora-executor/src/observer.rs``
In ``crates/taktora-executor/src/executor.rs``
Verification
|
PREEMPT_RT validation harness¶
Detailed design for the PREEMPT_RT validation harness sub-feature (PREEMPT_RT validation harness (FEAT_0022)). The harness is packaged as an out-of-tree cargo bin and consumes the Scan-cycle observability (FEAT_0021) telemetry push channel as its sole measurement path.
Context. Documented worst-case jitter (REQ_0110) requires a documented worst-case jitter envelope. The natural ASPICE / industrial pattern is to wire a benchmark gate into CI so regressions block merge. Cloud GitHub-hosted runners do not run PREEMPT_RT and cannot be made to do so without self-hosting. A self-hosted PREEMPT_RT runner for a single-maintainer personal project carries ongoing infra cost (host availability, kernel updates, runner-agent updates). Decision. Package the harness as an out-of-tree cargo bin
under Alternatives considered.
Consequences. ✅ Zero ongoing infra cost; runs are on-demand by the maintainer. ✅ The harness path is identical to the production telemetry path (per Harness consumes runtime te... (REQ_0113)), so the manual run is representative of production behaviour. ❌ Regressions can land between manual runs. Mitigated partly by Allocation-free telemetry u... (TEST_0194) (allocation-free telemetry update) and Overrun counter increments ... (TEST_0192) (overrun counter correctness) staying in regular CI; what the harness uniquely validates is the absolute envelope, not behavioural correctness. |
Workspace member CLI shape: cargo xtask preempt-rt-bench \
--load-profile {idle,cpu-stress,cyclictest-coexist} \
--cycle-count <N> \
--task-count <K> \
--scan-period-us <P>
The harness installs a custom |
New workspace member ``xtask/preempt-rt/``
New document ``docs/preempt-rt-procedure.md`` (deferred to the implementation phase — written when the first measurement run is staged so the procedure can reflect the actual host). Sections planned:
Verification
|