- Fixed circular dependency detection by implementing a basic DFS

Signed-off-by: Paul Beckingham <paul@beckingham.net>
This commit is contained in:
Wilhelm Schuermann 2011-12-18 12:10:23 -05:00 committed by Paul Beckingham
parent 30a97f5f52
commit 726fc33fe4
2 changed files with 29 additions and 43 deletions

View file

@ -62,6 +62,7 @@ The following submitted code, packages or analysis, and deserve special thanks:
Greg Grossmeier Greg Grossmeier
Barton Meeks Barton Meeks
Martin Klepsch Martin Klepsch
Wim Schuermann
Thanks to the following, who submitted detailed bug reports and excellent Thanks to the following, who submitted detailed bug reports and excellent
suggestions: suggestions:
@ -105,7 +106,6 @@ suggestions:
Jonathan Hankins Jonathan Hankins
Andreas Kalex Andreas Kalex
Adam Wolk Adam Wolk
Wim Schuermann
Tom Duffy Tom Duffy
Miguel de Val Borro Miguel de Val Borro
Yann Davin Yann Davin

View file

@ -25,12 +25,12 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#define L10N // Localization complete. #define L10N // Localization complete.
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <stack>
#include <Context.h> #include <Context.h>
#include <text.h> #include <text.h>
#include <util.h> #include <util.h>
@ -39,9 +39,6 @@
extern Context context; extern Context context;
////////////////////////////////////////////////////////////////////////////////
static bool followUpstream (const Task&, const Task&, std::vector <std::string>&);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// A task is blocked if it depends on tasks that are pending or waiting. // A task is blocked if it depends on tasks that are pending or waiting.
// //
@ -116,49 +113,38 @@ void dependencyGetBlocking (const Task& task, std::vector <Task>& blocking)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Terminology: // Returns true if the supplied task adds a cycle to the dependency chain.
// --> if a depends on b, then it can be said that a --> b
// Head if a --> b, then b is the head
// Tail if a --> b, then a is the tail
//
// Algorithm:
// Keep walking the chain, recording the links (a --> b, b --> c, ...) until
// either the end of the chain is found (therefore not circular), or the chain
// loops and a repeat link is spotted (therefore circular).
bool dependencyIsCircular (const Task& task) bool dependencyIsCircular (const Task& task)
{ {
std::vector <std::string> seen; std::stack <Task> s;
return followUpstream (task, task, seen); std::vector <int> deps_current;
}
//////////////////////////////////////////////////////////////////////////////// std::string task_uuid = task.get ("uuid");
// Returns true if a task is encountered twice in a chain walk, and therefore
// indicates circularity. Recursive. s.push (task);
static bool followUpstream ( while (!s.empty ())
const Task& task,
const Task& original,
std::vector <std::string>& seen)
{
std::vector <Task> blocking;
dependencyGetBlocking (task, blocking);
std::vector <Task>::iterator b;
for (b = blocking.begin (); b != blocking.end (); ++b)
{ {
std::string link = task.get ("uuid") + " -> " + b->get ("uuid"); Task& current = s.top ();
current.getDependencies (deps_current);
// Have we seen this link before? If so, circularity has been detected. // This is a basic depth first search that always terminates given the
if (std::find (seen.begin (), seen.end (), link) != seen.end ()) // assumption that any cycles in the dependency graph must have been
// introduced by the task that is being checked.
// Since any previous cycles would have been prevented by this very
// function, this is a reasonable assumption.
for (unsigned int i = 0; i < deps_current.size (); i++)
{
context.tdb2.get (deps_current[i], current);
if (task_uuid == current.get ("uuid"))
{
// Cycle found, initial task reached for the second time!
return true; return true;
}
seen.push_back (link); s.push (current);
}
// Use 'original' over '*b' if they both refer to the same task, otherwise s.pop ();
// '*b' is from TDB2's committed list, and lacks recent modifications.
if (followUpstream (
(b->get ("uuid") == original.get ("uuid") ? original : *b),
original,
seen))
return true;
} }
return false; return false;