/* Copyright 2012 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #pragma once #include #include #include #include "mongo/base/disallow_copying.h" #include "mongo/base/initializer_function.h" #include "mongo/base/status.h" #include "mongo/platform/unordered_map.h" #include "mongo/platform/unordered_set.h" namespace mongo { /** * Representation of a dependency graph of "initialization operations." * * Each operation has a unique name, a function object implementing the operation's behavior, * and a set of prerequisite operations, which may be empty. A legal graph contains no cycles. * * Instances of this class are used in two phases. In the first phase, the graph is constructed * by repeated calls to addInitializer(). In the second phase, a user calls the topSort() * method to produce an initialization order that respects the dependencies among operations, and * then uses the getInitializerFunction() to get the behavior function for each operation, in * turn. * * Concurrency Notes: The user is responsible for synchronization. Multiple threads may * simultaneously call the const functions, getInitializerFunction and topSort, on the same * instance of InitializerDependencyGraph. However, no thread may call addInitializer while any * thread is executing those functions or addInitializer on the same instance. */ class InitializerDependencyGraph { MONGO_DISALLOW_COPYING(InitializerDependencyGraph); public: InitializerDependencyGraph(); ~InitializerDependencyGraph(); /** * Add a new initializer node, named "name", to the dependency graph, with the given * behavior, "fn", and the given "prerequisites" (input dependencies) and "dependents" * (output dependencies). * * If "!fn" (fn is NULL in function pointer parlance), returns status with code * ErrorCodes::badValue. If "name" is a duplicate of a name already present in the graph, * returns "ErrorCodes::duplicateKey". Otherwise, returns Status::OK() and adds the new node * to the graph. Note that cycles in the dependency graph are not discovered in this phase. * Rather, they're discovered by topSort, below. */ Status addInitializer(const std::string& name, const InitializerFunction& fn, const std::vector& prerequisites, const std::vector& dependents); /** * Given a dependency operation node named "name", return its behavior function. Returns * a value that evaluates to "false" in boolean context, otherwise. */ InitializerFunction getInitializerFunction(const std::string& name) const; /** * Construct a topological sort of the dependency graph, and store that order into * "sortedNames". Returns Status::OK() on success. * * If the graph contains a cycle, returns ErrorCodes::graphContainsCycle, and "sortedNames" * is an ordered sequence of nodes involved in a cycle. In this case, the first and last * element of "sortedNames" will be equal. * * If any node in the graph names a prerequisite that was never added to the graph via * addInitializer, this function will return ErrorCodes::badValue. * * Any other return value indicates an internal error, and should not occur. */ Status topSort(std::vector* sortedNames) const; private: struct NodeData { InitializerFunction fn; unordered_set prerequisites; }; typedef unordered_map NodeMap; typedef NodeMap::value_type Node; /** * Helper function to recursively top-sort a graph. Used by topSort(). */ static Status recursiveTopSort(const NodeMap& nodeMap, const Node& currentNode, std::vector* inProgressNodeNames, unordered_set* visitedNodeNames, std::vector* sortedNames); /** * Map of all named nodes. Nodes named as prerequisites or dependents but not explicitly * added via addInitializer will either be absent from this map or be present with * NodeData::fn set to a false-ish value. */ NodeMap _nodes; }; } // namespace mongo