How to Detect and Manage Cyclic Dependencies in ApraPipes Pipeline Configurations
ApraPipes detects cyclic dependencies using a depth-first search with three-color vertex coloring on an adjacency list built from the pipeline's JSON connections array, reporting the exact cycle path as a GRAPH_HAS_CYCLE validation error.
ApraPipes is an open-source C++ framework for building high-performance multimedia processing pipelines using declarative JSON configurations. Because pipelines are modeled as directed graphs where modules are nodes and connections are edges, cyclic dependencies can create deadlocks or infinite processing loops that must be caught before runtime.
How Cyclic Dependencies Are Detected in ApraPipes
The framework validates pipeline configurations by treating the connections array as a directed graph and applying a standard graph cycle detection algorithm implemented in base/src/declarative/PipelineValidator.cpp.
Building the Adjacency List
During validation, the validator constructs an adjacency list where each module ID maps to its outgoing connections. This happens between lines 792-800 in PipelineValidator.cpp.
// Build adjacency list for cycle detection (PipelineValidator.cpp)
std::map<std::string, std::vector<std::string>> adjacency;
for (const auto& conn : desc.connections) {
adjacency[conn.from_module].push_back(conn.to_module);
}
Depth-First Search with Three-Color Vertex Coloring
The validator implements a recursive DFS using a color map to track vertex states (lines 805-830). Each module starts as White (0) (unvisited), transitions to Gray (1) when entering the recursion stack, and becomes Black (2) after processing all descendants.
// White = 0, Gray = 1, Black = 2
std::map<std::string, int> color;
for (const auto& module : desc.modules) {
color[module.instance_id] = 0;
}
When the algorithm encounters a Gray neighbor, it has found a back-edge indicating a cycle. The current path is captured and formatted as a human-readable string (e.g., A → B → C → A).
if (color[neighbor] == 1) {
// Found cycle – neighbor is in current path
path.push_back(neighbor);
return true;
}
Cycle Reporting and Error Messages
Upon detecting a cycle, the validator creates an Issue of type GRAPH_HAS_CYCLE with a descriptive message and remediation suggestion (lines 831-845). Only the first detected cycle is reported per validation run.
The error message format includes the exact cycle path:
Error [GRAPH_HAS_CYCLE] pipeline: Cycle detected in pipeline: A -> B -> A
Suggestion: Remove one of the connections to break the cycle
Managing and Resolving Cyclic Dependencies
Once detected, cyclic dependencies must be resolved before the pipeline can execute. ApraPipes provides both preventive and corrective mechanisms.
Design-Time Prevention
While the JSON schema (docs/declarative-pipeline/pipeline-schema.json) defines the structure of modules and connections, it does not enforce acyclicity at the schema level. The PipelineValidator serves as the authoritative gate, rejecting cyclic configurations during the validation phase before any modules are instantiated.
Breaking Cycles in Existing Configurations
To resolve a detected cycle, modify the pipeline configuration by:
- Removing one connection that closes the loop (e.g., delete the edge that creates the back-reference)
- Re-routing data flow through an intermediate module such as a buffer or transformer to transform the cyclic graph into a Directed Acyclic Graph (DAG)
Automated Validation in CI/CD
Integrate the aprapipes_cli validate command into continuous integration pipelines to catch cyclic dependencies automatically:
aprapipes_cli validate pipeline_config.json
# Returns non-zero exit code if GRAPH_HAS_CYCLE or other issues exist
This ensures that only valid, acyclic pipeline configurations reach production environments.
Code Examples
Valid Acyclic Pipeline Configuration
The following JSON defines a linear processing chain with no cyclic dependencies:
{
"modules": [
{ "instance_id": "src", "module_type": "FileReaderModule" },
{ "instance_id": "proc", "module_type": "ResizeModule" },
{ "instance_id": "sink", "module_type": "VideoWriterModule" }
],
"connections": [
{ "from_module": "src", "from_port": "output", "to_module": "proc", "to_port": "input" },
{ "from_module": "proc", "from_port": "output", "to_module": "sink", "to_port": "input" }
]
}
Validation succeeds with no cycle detection triggers:
aprapipes_cli validate examples/basic/split_pipeline.json
# → Validation successful (no issues)
Cyclic Configuration and Validation Output
Introducing a back-edge creates a cycle between modules A and B:
{
"modules": [
{ "instance_id": "A", "module_type": "FileReaderModule" },
{ "instance_id": "B", "module_type": "ResizeModule" }
],
"connections": [
{ "from_module": "A", "from_port": "output", "to_module": "B", "to_port": "input" },
{ "from_module": "B", "from_port": "output", "to_module": "A", "to_port": "input" }
]
}
The validator detects and reports the specific cycle path:
aprapipes_cli validate cyclic_example.json
Error [GRAPH_HAS_CYCLE] pipeline: Cycle detected in pipeline: A -> B -> A
Suggestion: Remove one of the connections to break the cycle
Programmatic Detection in C++
For applications that validate pipelines programmatically, use the PipelineValidator API directly:
#include "aprapipes/base/src/declarative/PipelineValidator.h"
aprapipes::declarative::PipelineDescription desc = loadFromJson("my_pipeline.json");
aprapipes::declarative::PipelineValidator validator;
auto result = validator.validate(desc);
if (!result.issues.empty()) {
for (const auto& issue : result.issues) {
if (issue.type == aprapipes::declarative::Issue::GRAPH_HAS_CYCLE) {
std::cout << "Cycle found: " << issue.message << std::endl;
}
}
}
Summary
- ApraPipes models pipelines as directed graphs where modules are vertices and connections are edges, requiring acyclicity for valid execution.
- Cycle detection uses DFS with three-color vertex coloring (White/Gray/Black) implemented in
base/src/declarative/PipelineValidator.cpp(lines 792-845). - Validation errors include the exact cycle path (e.g.,
A -> B -> A) and actionable remediation suggestions. - Resolution strategies include removing back-edges or re-routing data through intermediate modules to form a DAG.
- Integration via CLI (
aprapipes_cli validate) or C++ API ensures cyclic dependencies are caught at design-time before runtime instantiation.
Frequently Asked Questions
What algorithm does ApraPipes use to detect cyclic dependencies?
ApraPipes uses a Depth-First Search (DFS) with three-color vertex coloring to detect cycles in pipeline configurations. The algorithm assigns each module a color state—White (0) for unvisited, Gray (1) for currently in the recursion stack, and Black (2) for fully processed—and identifies a cycle when it encounters a Gray neighbor, indicating a back-edge in the graph.
Where is the cycle detection logic implemented in the source code?
The cycle detection logic is implemented in base/src/declarative/PipelineValidator.cpp between lines 792 and 845. This section contains the adjacency list construction, color map initialization, recursive DFS traversal, and the GRAPH_HAS_CYCLE error reporting mechanism that validates pipeline configurations before runtime execution.
How does ApraPipes report cyclic dependency errors?
When a cycle is detected, ApraPipes generates a validation Issue of type GRAPH_HAS_CYCLE containing a human-readable message that specifies the exact cycle path (e.g., A -> B -> C -> A). The error includes a suggestion to remove one of the connections to break the cycle, and the CLI returns a non-zero exit code to signal validation failure in automated workflows.
Can cyclic dependencies be prevented at the JSON schema level?
No, the JSON schema defined in docs/declarative-pipeline/pipeline-schema.json validates the structure of modules and connections but cannot enforce graph acyclicity. The PipelineValidator serves as the authoritative gatekeeper that performs the computational validation required to ensure the pipeline forms a Directed Acyclic Graph (DAG) before instantiation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →