32enum eCyclicCheckVisitedState {
44 Relation *via_relation;
47struct CyclesSolverState {
48 CyclesSolverState(Depsgraph *graph) : graph(graph) {}
51 if (num_cycles != 0) {
52 CLOG_WARN(&
LOG,
"Detected %d dependency cycles", num_cycles);
56 Stack<StackEntry> traversal_stack;
60inline void set_node_visited_state(
Node *node, eCyclicCheckVisitedState
state)
62 node->custom_flags = (node->custom_flags & ~0x3) |
int(
state);
65inline eCyclicCheckVisitedState get_node_visited_state(
Node *node)
67 return (eCyclicCheckVisitedState)(node->custom_flags & 0x3);
70inline void set_node_num_visited_children(
Node *node,
int num_children)
72 node->custom_flags = (node->custom_flags & 0x3) | (num_children << 2);
75inline int get_node_num_visited_children(
Node *node)
77 return node->custom_flags >> 2;
85 entry.via_relation =
nullptr;
86 state->traversal_stack.push(entry);
87 set_node_visited_state(node, NODE_IN_STACK);
91void schedule_leaf_nodes(CyclesSolverState *
state)
94 bool has_inlinks =
false;
95 for (
Relation *rel : node->inlinks) {
100 node->custom_flags = 0;
101 if (has_inlinks ==
false) {
102 schedule_node_to_stack(
state, node);
105 set_node_visited_state(node, NODE_NOT_VISITED);
113bool schedule_non_checked_node(CyclesSolverState *
state)
116 if (get_node_visited_state(node) == NODE_NOT_VISITED) {
117 schedule_node_to_stack(
state, node);
124bool check_relation_can_murder(
Relation *relation)
132Relation *select_relation_to_murder(
Relation *relation, StackEntry *cycle_start_entry)
138 if (check_relation_can_murder(relation)) {
141 StackEntry *current = cycle_start_entry;
143 while (current->node != to_node) {
144 if (check_relation_can_murder(current->via_relation)) {
145 return current->via_relation;
147 current = current->from;
153void solve_cycles(CyclesSolverState *
state)
155 Stack<StackEntry> &traversal_stack =
state->traversal_stack;
156 while (!traversal_stack.
is_empty()) {
157 StackEntry *entry = &traversal_stack.
peek();
159 bool all_child_traversed =
true;
160 const int num_visited = get_node_num_visited_children(node);
161 for (
int i = num_visited;
i < node->outlinks.size();
i++) {
165 eCyclicCheckVisitedState to_state = get_node_visited_state(to);
166 if (to_state == NODE_IN_STACK) {
167 std::string cycle_str =
" " + to->full_identifier() +
" depends on\n " +
168 node->full_identifier() +
" via '" + rel->name +
"'\n";
169 StackEntry *current = entry;
170 while (current->node != to) {
172 cycle_str +=
" " + current->from->node->full_identifier() +
" via '" +
173 current->via_relation->name +
"'\n";
174 current = current->from;
176 CLOG_WARN(&
LOG,
"Dependency cycle detected:\n%s", cycle_str.c_str());
177 Relation *sacrificial_relation = select_relation_to_murder(rel, entry);
181 else if (to_state == NODE_NOT_VISITED) {
182 StackEntry new_entry;
184 new_entry.from = entry;
185 new_entry.via_relation = rel;
186 traversal_stack.
push(new_entry);
187 set_node_visited_state(node, NODE_IN_STACK);
188 all_child_traversed =
false;
189 set_node_num_visited_children(node,
i);
194 if (all_child_traversed) {
195 set_node_visited_state(node, NODE_VISITED);
196 traversal_stack.
pop();
205 CyclesSolverState
state(graph);
207 schedule_leaf_nodes(&
state);
208 solve_cycles(&
state);
213 while (schedule_non_checked_node(&
state)) {
214 solve_cycles(&
state);
#define CLOG_WARN(clg_ref,...)
void push(const T &value)
void deg_graph_detect_cycles(Depsgraph *graph)