summaryrefslogtreecommitdiff
path: root/src/mongo/base/initializer_dependency_graph.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/base/initializer_dependency_graph.cpp')
-rw-r--r--src/mongo/base/initializer_dependency_graph.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/mongo/base/initializer_dependency_graph.cpp b/src/mongo/base/initializer_dependency_graph.cpp
new file mode 100644
index 00000000000..90e259167f4
--- /dev/null
+++ b/src/mongo/base/initializer_dependency_graph.cpp
@@ -0,0 +1,149 @@
+/* Copyright 2012 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mongo/base/initializer_dependency_graph.h"
+
+#include <algorithm>
+#include <iterator>
+
+namespace mongo {
+
+ InitializerDependencyGraph::InitializerDependencyGraph() {}
+ InitializerDependencyGraph::~InitializerDependencyGraph() {}
+
+ Status InitializerDependencyGraph::addInitializer(const std::string& name,
+ const InitializerFunction& fn,
+ const std::vector<std::string>& prerequisites,
+ const std::vector<std::string>& dependents) {
+ if (!fn)
+ return Status(ErrorCodes::BadValue, "Illegal to supply a NULL function");
+
+ NodeData& newNode = _nodes[name];
+ if (newNode.fn) {
+ return Status(ErrorCodes::DuplicateKey, name);
+ }
+
+ newNode.fn = fn;
+
+ for (size_t i = 0; i < prerequisites.size(); ++i) {
+ newNode.prerequisites.insert(prerequisites[i]);
+ }
+
+ for (size_t i = 0; i < dependents.size(); ++i) {
+ _nodes[dependents[i]].prerequisites.insert(name);
+ }
+
+ return Status::OK;
+ }
+
+ InitializerFunction InitializerDependencyGraph::getInitializerFunction(
+ const std::string& name) const {
+
+ NodeMap::const_iterator iter = _nodes.find(name);
+ if (iter == _nodes.end())
+ return InitializerFunction();
+ return iter->second.fn;
+ }
+
+ Status InitializerDependencyGraph::topSort(std::vector<std::string>* sortedNames) const {
+ /*
+ * This top-sort is implemented by performing a depth-first traversal of the dependency
+ * graph, once for each node. "visitedNodeNames" tracks the set of node names ever visited,
+ * and it is used to prune each DFS. A node that has been visited once on any DFS is never
+ * visited again. Complexity of this implementation is O(n+m) where "n" is the number of
+ * nodes and "m" is the number of prerequisite edges. Space complexity is O(n), in both
+ * stack space and size of the "visitedNodeNames" set.
+ *
+ * "inProgressNodeNames" is used to detect and report cycles.
+ */
+
+ std::vector<std::string> inProgressNodeNames;
+ unordered_set<std::string> visitedNodeNames;
+
+ sortedNames->clear();
+ for (NodeMap::const_iterator iter = _nodes.begin(), end = _nodes.end();
+ iter != end; ++iter) {
+
+ Status status = recursiveTopSort(_nodes,
+ *iter,
+ &inProgressNodeNames,
+ &visitedNodeNames,
+ sortedNames);
+ if (Status::OK != status)
+ return status;
+ }
+ return Status::OK;
+ }
+
+ Status InitializerDependencyGraph::recursiveTopSort(
+ const NodeMap& nodeMap,
+ const Node& currentNode,
+ std::vector<std::string>* inProgressNodeNames,
+ unordered_set<std::string>* visitedNodeNames,
+ std::vector<std::string>* sortedNames) {
+
+ /*
+ * The top sort is performed by depth-first traversal starting at each node in the
+ * dependency graph, short-circuited any time a node is seen that has already been visited
+ * in any traversal. "visitedNodeNames" is the set of nodes that have been successfully
+ * visited, while "inProgressNodeNames" are nodes currently in the exploration chain. This
+ * structure is kept explicitly to facilitate cycle detection.
+ *
+ * This function implements a depth-first traversal, and is called once for each node in the
+ * graph by topSort(), above.
+ */
+
+ if ((*visitedNodeNames).count(currentNode.first))
+ return Status::OK;
+
+ if (!currentNode.second.fn)
+ return Status(ErrorCodes::BadValue, currentNode.first);
+
+ inProgressNodeNames->push_back(currentNode.first);
+
+ std::vector<std::string>::iterator firstOccurence = std::find(
+ inProgressNodeNames->begin(), inProgressNodeNames->end(), currentNode.first);
+ if (firstOccurence + 1 != inProgressNodeNames->end()) {
+ sortedNames->clear();
+ std::copy(firstOccurence, inProgressNodeNames->end(), std::back_inserter(*sortedNames));
+ return Status(ErrorCodes::GraphContainsCycle, "Cycle in dependency graph");
+ }
+
+ for (unordered_set<std::string>::const_iterator
+ iter = currentNode.second.prerequisites.begin(),
+ end = currentNode.second.prerequisites.end();
+ iter != end; ++iter) {
+
+ NodeMap::const_iterator nextNode = nodeMap.find(*iter);
+ if (nextNode == nodeMap.end())
+ return Status(ErrorCodes::BadValue, *iter);
+
+ Status status = recursiveTopSort(nodeMap,
+ *nextNode,
+ inProgressNodeNames,
+ visitedNodeNames,
+ sortedNames);
+ if (Status::OK != status)
+ return status;
+ }
+ sortedNames->push_back(currentNode.first);
+ if (inProgressNodeNames->back() != currentNode.first)
+ return Status(ErrorCodes::InternalError, "inProgressNodeNames stack corrupt");
+ inProgressNodeNames->pop_back();
+ visitedNodeNames->insert(currentNode.first);
+ return Status::OK;
+ }
+
+} // namespace mongo