Jump to content

Running and Stopping

From RidgeRun Developer Wiki

🚧 Documentation is under development

The Holoscan SDK guide is currently under active development. Some sections may be incomplete or change without notice.

Questions? Contact RidgeRun or email to support@ridgerun.com.

Contents Pin





Running and Stopping

After a graph has been compiled into an accepted ExecutionPlan, the application chooses how to start it and how to decide that the work is complete. A command-line tool often uses blocking execution. An application that also manages a user interface, device control, or another service usually keeps the run in an asynchronous session.

Both choices use the same order: start the accepted plan, observe the application, request a cooperative stop when appropriate, wait for cleanup, and inspect the terminal result.

Start with the execution choice

For a simple application whose main work is the graph, use the blocking holoscan::run() function:

// plan is an accepted holoscan::ExecutionPlan.
return holoscan::run(plan);

The call keeps the current thread in the run until the graph reaches a terminal state. A zero return value represents natural or explicitly requested successful completion. A stopped or failed run returns nonzero.

When the surrounding application must continue doing other work, use holoscan::run_async(). It returns a move-only RunSession that owns the runtime work:

auto session = holoscan::run_async(plan);
// The application can do other work while the graph runs.

Keep the session owner alive while the graph is active. Moving the session transfers the stop and wait authority to the new owner.

Use cooperative stop and wait

The RunSession::request_stop() function asks the graph to stop without waiting. The RunSession::wait() function joins the run and cleanup. Together they give the application a clear shutdown point:

#include <chrono>
#include <thread>

int run_for(const holoscan::ExecutionPlan& plan,
            std::chrono::milliseconds duration) {
  auto session = holoscan::run_async(plan);
  std::this_thread::sleep_for(duration);
  session.request_stop();
  session.wait();

  const auto outcome = session.termination();
  return outcome == holoscan::RunTermination::kFailed ? 1 : 0;
}

The example uses a timer to keep the control path small. A real application can request the same stop when its own shutdown event arrives. Stop is cooperative: callbacks that are already running must return, and a nonterminating CUDA kernel or callback can keep wait() blocked.

Read the terminal outcome

The RunTermination enum summarizes the session after cleanup:

Outcome Meaning
kCompleted Finite execution and mandatory cleanup completed successfully.
kRequestedCompleted An operator requested whole-run success and cleanup completed successfully.
kStopped The application requested a cooperative stop and cleanup completed successfully.
kFailed Startup, execution, or mandatory cleanup failed.
kRunning Work is still active. It is not a terminal result.

Inspect session.termination() after wait(). A clean stop is expected when the application reached its own stopping condition. Treat kFailed as an error and read the retained diagnostics before returning.

An operator can request whole-run success through ExecutionContext::request_completion() from its active compute callback. That produces kRequestedCompleted. It does not promise end-of-stream propagation or delivery of queued samples, so use it only when ending the complete run at that boundary is acceptable.

Choose who owns process signals

Plain run_async(plan) leaves signal handling to the application. Blocking run(plan) uses the SDK's managed signal policy on Linux. If another framework already owns SIGINT or SIGTERM, call run(plan, RunSignalPolicy::kCallerManaged) and keep signal ownership in that framework.

For asynchronous execution with SDK-managed signals, create ManagedShutdownSignals::create() on the initial application thread before starting participant threads. Pass the open domain to run_async(plan, signals), keep it alive until the session has stopped, and close it on its creator thread.

A managed signal requests the same cooperative stop described above. It does not force a callback or kernel to finish. Managed signal admission is a Linux process concern, so keep it after the basic run path unless the application embeds another signal owner.

Read diagnostics after cleanup

When a run fails, inspect session.diagnostics() after wait(). The records identify the observed failure stage and provide structured fields for program decisions. Use the configured diagnostic formatter for human-readable output, but do not branch on log prose.

Separate the stage that failed:

Observation First place to investigate
The application does not configure or compile. The SDK installation, CMake prefix, compiler, and example source.
compile(graph) rejects the graph. The graph connections, operator contracts, payload types, and plan diagnostics.
The plan is accepted but the session fails before callbacks run. Runtime bindings, device availability, model files, and package components.
A callback runs and then the session fails. The operator result, data access, device operation, and retained diagnostics.


Session ownership
Never call wait() or destroy the owning RunSession from a runtime compute, lifecycle, or dispatcher callback. Those operations belong to application control code outside the run.


Related examples and concepts

Examples

Concepts

Official documentation

NVIDIA Running and Stopping documentation expands the official run-control guidance.


Cookies help us deliver our services. By using our services, you agree to our use of cookies.