Prioritize Control Commands
Goal
Queue urgent ControlCommand work before ordinary frame analysis in the same robot pipeline, while retaining result and exception observation through futures.
Recommended approach
Use submit_priority(priority, task). Priority ranges from 0 (low) to 3 (critical); ordinary work uses the default priority.
#include <iostream>
#include <string>
#include <executor/executor.hpp>
int main() {
executor::Executor executor;
executor::ExecutorConfig config;
config.min_threads = 1;
config.max_threads = 1;
if (!executor.initialize_ex(config)) {
return 1;
}
auto analysis = executor.submit_priority(0, [] { return std::string{"analysis"}; });
auto control = executor.submit_priority(2, [] { return std::string{"control"}; });
const auto analysis_result = analysis.get();
const auto control_result = control.get();
std::cout << "priority tasks=" << analysis_result << "," << control_result << '\n';
executor.shutdown();
return analysis_result == "analysis" && control_result == "control" ? 0 : 1;
}./build/examples/tutorial/tutorial_02_priorityExpected output:
priority tasks=analysis,controlsubmit_priority() still returns a future. Priority only changes how waiting work is selected: it cannot preempt a low-priority task already running. Multiple workers, running work, and same-priority FIFO all affect observed completion order, so this output is not a scheduling proof.
Inputs, ownership, and failure
The full form is submit_priority(priority, fn, args...); only the first argument is scheduling metadata. Callable and argument lifetime rules are identical to submit(fn, args...).
ControlCommand command = read_command();
auto applied = executor.submit_priority(3, apply_control_command, command);command is saved by value. Increasing priority does not make a raw this pointer, a borrowed pointer, or shared mutable state safe. Define one application-wide priority vocabulary: <= 0 is LOW, 1 NORMAL, 2 HIGH, and >= 3 CRITICAL. A constant stream of critical work can starve ordinary work.
Keep the returned futures. Empty work and submission after shutdown produce observable rejection or exception results; handle an execution failure at that task's future.get().
Test the boundary
- Start a blocking LOW task, then submit CRITICAL work: the latter cannot preempt work already running.
- Throw from the control task: its future and failure status observe it independently of analysis success.
- Fill a small queue with critical work: observe rejection, queue depth, and ordinary-task wait time.
- During shutdown, stop producers, consume held futures, then drain Executor within a budget.
If a command must take effect within a fixed time, measure queue latency and define timeout/degradation behavior. Critical priority alone is not an acceptance criterion. For fixed periods or jitter budgets, use the dedicated real-time path rather than pool priority.