Runtime modes
Current thread, background thread and manual update.
Runtime modes
This chapter applies only to applications that need to continuously advance background work such
as Retargeting or automatic reconnection. When only reading the raw pose/IMU, there is no need to
choose a runtime: after a successful start() call, data is published with valid device frames.
When scheduling is needed, choose one of three mutually exclusive run modes: the current thread,
a background thread, or manual update. next() and C++20 coroutines are ways to wait for a single
data item, not a fourth run mode.
Running continuously on the current thread
run_forever() runs on the current thread and suits command-line programs with a simple
lifecycle:
glove.start();
glove.run_forever();run_forever() suits programs that keep running on the current thread. The public command-line
examples end the process with Ctrl+C; when the application needs to stop it actively, use the
background thread mode below.
Running on a background thread
Use this when the main thread must continue running the UI, the application event loop, or safe-stop coordination:
glove.run_background();
// The main thread handles other work.
glove.request_stop();
glove.join();join() waits for the background run to finish and reports runtime errors. run_background()
does not implicitly connect or start devices.
Manual update
When the application owns its own scheduling loop, call update() repeatedly to perform one SDK
task scheduling pass:
const auto update = glove.update();
do_application_work();
if (update.has_next_task()) {
const auto result = update.sleep();
}UpdateResult provides tick(), ran_task_count(), and has_next_task(). sleep() waits for
subsequent work based on the current time and returns SleepResult::ready, stopped, or
no_tasks. Each update result's wait token can be used only once. Raw pose/IMU is published with
device frames and does not depend on update().
Blocking wait and cancellation
next() returns a move-only NextOperation<T> that waits for the next data item after
registration succeeds:
auto pending = device.pose().next();
auto sample = pending.get();next() can be used after start(); for continuous data processing, subscribe() registered
before starting is still preferred. IMU is awaited in the same way as pose.
get() blocks until data arrives or the operation terminates, and completes internal
subscription cleanup before returning. Raw pose/IMU can wake the wait directly after start();
when waiting for retargeting output, the SDK scheduling must be advanced by background mode or
another thread. Use cancel() to cancel synchronously:
auto pending = device.pose().next();
pending.cancel();
try {
pending.get();
} catch (const std::system_error& error) {
if (error.code() != std::errc::operation_canceled) {
throw;
}
}NextOperation<T> can be consumed only once. Calling it again on a moved-from or already
consumed operation throws LifecycleError. For the overview of read methods, see
Data channels & read methods.
C++20 coroutines
After additionally linking qnbot::core_coro and including <qnbot/coro.hpp>, you can change an
existing single wait to co_await qnbot::coro::next(...):
qnbot::Sample<qnbot::GlovePose> sample =
co_await qnbot::coro::next(
device.pose().next(), executor, stop_token);executor is a std::shared_ptr<qnbot::coro::DeferredExecutor>. Its post() must satisfy the
following contract:
- It receives each task once and eventually executes it once.
- It returns quickly and does not wait for the task execution to finish.
- It does not execute the task inline on the callback thread that calls
post(). post()does not throw.
The NextOperation<T> passed in can still be consumed only once. A std::stop_token that has
already requested stop results in std::errc::operation_canceled.
The application must ensure that the relevant executor, stop token, and task objects stay valid
while the coroutine has not yet completed or been cancelled; do not concurrently destroy a
coroutine while an executor callback is still running. For complete, directly usable handling of
the real glove lifecycle, see async_runtime.cpp.