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

PolicyBehavior
Miss::WarnLog warning, continue (default)
Miss::SkipSkip this tick, resume next
Miss::SafeModeCall enter_safe_state() on the node
Miss::StopStop 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_RT kernel (recommended but not required)
  • CAP_SYS_NICE capability 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

MetricValue
FFI overhead15 ns
Scheduler tick (1 node)248 ns median
Throughput2.89M ticks/sec
ASANZero errors