C++ Real-Time Guide
HORUS provides built-in real-time support. Set .rate(), .budget(), or .deadline() on a node and the scheduler automatically upgrades it to RT execution. No manual thread management.
Execution Classes
using namespace horus::literals;
horus::Scheduler sched;
sched.tick_rate(1000_hz).prefer_rt();
// RT — auto-detected from rate/budget/deadline
sched.add("motor")
.rate(1000_hz)
.budget(800_us) // max 800us per tick
.deadline(950_us) // must finish by 950us
.on_miss(horus::Miss::SafeMode) // enter safe state on overrun
.pin_core(2) // pin to CPU core 2
.priority(90) // SCHED_FIFO priority 90
.order(0) // runs first
.tick([&] { /* motor control */ })
.build();
// Compute — thread pool for CPU-heavy work
sched.add("planner")
.compute()
.order(50)
.tick([&] { /* path planning */ })
.build();
// Event — triggered by topic update
sched.add("estop_handler")
.on("emergency.stop")
.tick([&] { /* handle e-stop */ })
.build();
// AsyncIo — for GPU/network I/O
sched.add("detector")
.async_io()
.order(60)
.tick([&] { /* ML inference */ })
.build();
// BestEffort — no timing guarantees
sched.add("logger")
.order(100)
.tick([&] { /* log data */ })
.build();
Miss Policies
| Policy | Behavior |
|---|---|
Miss::Warn | Log warning, continue (default) |
Miss::Skip | Skip this tick, resume next |
Miss::SafeMode | Call enter_safe_state() on the node |
Miss::Stop | Stop the entire scheduler |
Node Lifecycle Hooks
class MotorCtrl : public horus::Node {
public:
MotorCtrl() : Node("motor") {
cmd_ = advertise<horus::msg::CmdVel>("motor.cmd");
}
void init() override {
// Called once before first tick
horus::log::info("motor", "Initialized, homing...");
}
void tick() override {
// Called every tick (1kHz with .rate(1000_hz))
}
void enter_safe_state() override {
// Called on Miss::SafeMode, watchdog timeout, or safety event
horus::msg::CmdVel stop{};
cmd_->send(stop);
horus::blackbox::record("motor", "Entered safe state");
}
private:
horus::Publisher<horus::msg::CmdVel>* cmd_;
};
Watchdog
sched.watchdog(5_s); // Global: any node stuck > 5s triggers action
sched.add("sensor")
.watchdog(500_ms) // Per-node: this node must tick within 500ms
.tick([&] { /* ... */ })
.build();
RT Requirements
For full RT (SCHED_FIFO):
- Linux with
PREEMPT_RTkernel (recommended but not required) CAP_SYS_NICEcapability or root- CPU governor set to
performance
Without RT kernel, HORUS degrades gracefully — .prefer_rt() logs warnings but continues. Use .require_rt() to fail if RT is unavailable.
Performance
| Metric | Value |
|---|---|
| FFI overhead | 15 ns |
| Scheduler tick (1 node) | 248 ns median |
| Throughput | 2.89M ticks/sec |
| ASAN | Zero errors |