diff options
author | Kevin Pulo <kevin.pulo@mongodb.com> | 2020-03-16 16:43:39 +1100 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-05-18 08:08:44 +0000 |
commit | 9f6af24864286343052497e827f06cba350c3c3e (patch) | |
tree | 1c33f84c8b8863604796a3d28bc107c3c46f0f24 | |
parent | d0e6f87351312a299a17f6d63e3f2f4db834ae1f (diff) | |
download | mongo-9f6af24864286343052497e827f06cba350c3c3e.tar.gz |
SERVER-46881 Forbid adding nodes to initializer graph after starting initializer execution
-rw-r--r-- | src/mongo/base/initializer.cpp | 52 | ||||
-rw-r--r-- | src/mongo/base/initializer.h | 9 | ||||
-rw-r--r-- | src/mongo/base/initializer_dependency_graph.cpp | 2 | ||||
-rw-r--r-- | src/mongo/base/initializer_dependency_graph.h | 40 | ||||
-rw-r--r-- | src/mongo/base/initializer_dependency_graph_test.cpp | 38 | ||||
-rw-r--r-- | src/mongo/base/initializer_test.cpp | 426 |
6 files changed, 456 insertions, 111 deletions
diff --git a/src/mongo/base/initializer.cpp b/src/mongo/base/initializer.cpp index df7a02587cd..6cff1611be0 100644 --- a/src/mongo/base/initializer.cpp +++ b/src/mongo/base/initializer.cpp @@ -38,6 +38,7 @@ #include "mongo/base/initializer_context.h" #include "mongo/util/assert_util.h" #include "mongo/util/quick_exit.h" +#include "mongo/util/str.h" namespace mongo { @@ -46,15 +47,20 @@ Initializer::~Initializer() {} Status Initializer::executeInitializers(const InitializerContext::ArgumentVector& args, const InitializerContext::EnvironmentMap& env) { - std::vector<std::string> sortedNodes; - Status status = _graph.topSort(&sortedNodes); - if (Status::OK() != status) - return status; + auto oldState = std::exchange(_lifecycleState, State::kInitializing); + invariant(oldState == State::kUninitialized, "invalid initializer state transition"); + + if (_sortedNodes.empty()) { + if (Status status = _graph.topSort(&_sortedNodes); !status.isOK()) { + return status; + } + } + _graph.freeze(); InitializerContext context(args, env); - for (size_t i = 0; i < sortedNodes.size(); ++i) { - InitializerDependencyNode* node = _graph.getInitializerNode(sortedNodes[i]); + for (const auto& nodeName : _sortedNodes) { + InitializerDependencyNode* node = _graph.getInitializerNode(nodeName); // If already initialized then this node is a legacy initializer without re-initialization // support. @@ -64,48 +70,52 @@ Status Initializer::executeInitializers(const InitializerContext::ArgumentVector auto const& fn = node->getInitializerFunction(); if (!fn) { return Status(ErrorCodes::InternalError, - "topSort returned a node that has no associated function: \"" + - sortedNodes[i] + '"'); + "topSort returned a node that has no associated function: \"" + nodeName + + '"'); } try { - status = fn(&context); + if (Status status = fn(&context); !status.isOK()) { + return status; + } } catch (const DBException& xcp) { return xcp.toStatus(); } - if (Status::OK() != status) - return status; - node->setInitialized(true); } + + oldState = std::exchange(_lifecycleState, State::kInitialized); + invariant(oldState == State::kInitializing, "invalid initializer state transition"); + return Status::OK(); } Status Initializer::executeDeinitializers() { - std::vector<std::string> sortedNodes; - Status status = _graph.topSort(&sortedNodes); - if (Status::OK() != status) - return status; + auto oldState = std::exchange(_lifecycleState, State::kDeinitializing); + invariant(oldState == State::kInitialized, "invalid initializer state transition"); DeinitializerContext context{}; // Execute deinitialization in reverse order from initialization. - for (auto it = sortedNodes.rbegin(), end = sortedNodes.rend(); it != end; ++it) { + for (auto it = _sortedNodes.rbegin(), end = _sortedNodes.rend(); it != end; ++it) { InitializerDependencyNode* node = _graph.getInitializerNode(*it); auto const& fn = node->getDeinitializerFunction(); if (fn) { try { - status = fn(&context); + if (Status status = fn(&context); !status.isOK()) { + return status; + } } catch (const DBException& xcp) { return xcp.toStatus(); } - if (Status::OK() != status) - return status; - node->setInitialized(false); } } + + oldState = std::exchange(_lifecycleState, State::kUninitialized); + invariant(oldState == State::kDeinitializing, "invalid initializer state transition"); + return Status::OK(); } diff --git a/src/mongo/base/initializer.h b/src/mongo/base/initializer.h index c7297abacbf..91b4c5e8183 100644 --- a/src/mongo/base/initializer.h +++ b/src/mongo/base/initializer.h @@ -73,7 +73,16 @@ public: Status executeDeinitializers(); private: + enum class State { + kUninitialized, + kInitializing, + kInitialized, + kDeinitializing, + }; + InitializerDependencyGraph _graph; + std::vector<std::string> _sortedNodes; + State _lifecycleState{State::kUninitialized}; }; /** diff --git a/src/mongo/base/initializer_dependency_graph.cpp b/src/mongo/base/initializer_dependency_graph.cpp index 0ed8c7318e0..24239cb4057 100644 --- a/src/mongo/base/initializer_dependency_graph.cpp +++ b/src/mongo/base/initializer_dependency_graph.cpp @@ -54,6 +54,8 @@ Status InitializerDependencyGraph::addInitializer(std::string name, if (!initFn) return Status(ErrorCodes::BadValue, "Illegal to supply a NULL function"); + invariant(!frozen()); + InitializerDependencyNode& newNode = _nodes[name]; if (newNode.initFn) { return Status(ErrorCodes::Error(50999), name); diff --git a/src/mongo/base/initializer_dependency_graph.h b/src/mongo/base/initializer_dependency_graph.h index a6422686e69..515f75cff26 100644 --- a/src/mongo/base/initializer_dependency_graph.h +++ b/src/mongo/base/initializer_dependency_graph.h @@ -71,16 +71,17 @@ private: * 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. + * Instances of this class are used in two phases. In the first phase, the graph is "unfrozen", + * which permits it to be constructed by repeated calls to addInitializer(). In the second phase, + * the graph is "frozen" by calling frozen(), which prevents the addition of any further + * initializers to the graph. A user can then call 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. + * simultaneously call the const functions, getInitializerFunction and topSort, on the same instance + * of InitializerDependencyGraph. However, no thread may call addInitializer or freeze while any + * thread is executing those functions, addInitializer or freeze on the same instance. */ class InitializerDependencyGraph { InitializerDependencyGraph(const InitializerDependencyGraph&) = delete; @@ -95,6 +96,8 @@ public: * behavior, "fn", and the given "prerequisites" (input dependencies) and "dependents" * (output dependencies). * + * The graph must not be frozen. + * * 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 @@ -128,6 +131,21 @@ public: */ Status topSort(std::vector<std::string>* sortedNames) const; + /** + * Called to mark the end of the period when nodes are allowed to be added to the graph. + * The graph is effectively read-only after this point. + */ + void freeze() { + _frozen = true; + } + + /** + * Returns true if this graph has been frozen. + */ + bool frozen() const { + return _frozen; + } + private: typedef stdx::unordered_map<std::string, InitializerDependencyNode> NodeMap; typedef NodeMap::value_type Node; @@ -138,6 +156,12 @@ private: * NodeData::fn set to a false-ish value. */ NodeMap _nodes; + + /** + * If true, then the graph is "frozen" (ie. effectively read-only), and adding initializer nodes + * is not allowed. + */ + bool _frozen{false}; }; } // namespace mongo diff --git a/src/mongo/base/initializer_dependency_graph_test.cpp b/src/mongo/base/initializer_dependency_graph_test.cpp index bfd4f114ab6..3194041d1a9 100644 --- a/src/mongo/base/initializer_dependency_graph_test.cpp +++ b/src/mongo/base/initializer_dependency_graph_test.cpp @@ -37,6 +37,7 @@ #include "mongo/base/init.h" #include "mongo/base/initializer_dependency_graph.h" +#include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" #define STRIP_PARENS_(...) __VA_ARGS__ @@ -363,5 +364,42 @@ TEST(InitializerDependencyGraphTest, TopSortShufflesChildren) { }); } +TEST(InitializerDependencyGraphTest, FreezeCausesFrozen) { + InitializerDependencyGraph graph; + ASSERT_FALSE(graph.frozen()); + graph.freeze(); + ASSERT_TRUE(graph.frozen()); + graph.freeze(); + ASSERT_TRUE(graph.frozen()); +} + +TEST(InitializerDependencyGraphTest, TopSortEmptyGraphWhileFrozen) { + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + graph.freeze(); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(0U, nodeNames.size()); +} + +TEST(InitializerDependencyGraphTest, TopSortGraphNoDepsWhileFrozen) { + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + graph.freeze(); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(3U, nodeNames.size()); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); +} + +DEATH_TEST(InitializerDependencyGraphTest, CannotAddWhenFrozen, "!frozen()") { + InitializerDependencyGraph graph; + graph.freeze(); + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); +} + } // namespace } // namespace mongo diff --git a/src/mongo/base/initializer_test.cpp b/src/mongo/base/initializer_test.cpp index 5656875d21e..86f281cc666 100644 --- a/src/mongo/base/initializer_test.cpp +++ b/src/mongo/base/initializer_test.cpp @@ -34,6 +34,7 @@ #include "mongo/base/init.h" #include "mongo/base/initializer.h" #include "mongo/base/initializer_dependency_graph.h" +#include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" /* @@ -52,145 +53,406 @@ #define STRIP_PARENS_(...) __VA_ARGS__ -#define ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ +#define ADD_INITIALIZER(GRAPH, NAME, INIT_FN, DEINIT_FN, PREREQS, DEPS) \ (GRAPH).addInitializer((NAME), \ - (FN), \ - DeinitializerFunction(), \ + (INIT_FN), \ + (DEINIT_FN), \ std::vector<std::string>{STRIP_PARENS_ PREREQS}, \ std::vector<std::string>{STRIP_PARENS_ DEPS}) -#define ASSERT_ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ - ASSERT_EQUALS(Status::OK(), ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS)) - - -#define CONSTRUCT_DEPENDENCY_GRAPH(GRAPH, FN0, FN1, FN2, FN3, FN4, FN5, FN6, FN7, FN8) \ - do { \ - InitializerDependencyGraph& _graph_ = (GRAPH); \ - ASSERT_ADD_INITIALIZER(_graph_, "n0", FN0, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n1", FN1, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n2", FN2, ("n0", "n1"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n3", FN3, ("n0", "n2"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n4", FN4, ("n2", "n1"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n5", FN5, ("n3", "n4"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n6", FN6, ("n4"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n7", FN7, ("n3"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n8", FN8, ("n5", "n6", "n7"), MONGO_NO_DEPENDENTS); \ +#define ASSERT_ADD_INITIALIZER(GRAPH, NAME, INIT_FN, DEINIT_FN, PREREQS, DEPS) \ + ASSERT_EQUALS(Status::OK(), ADD_INITIALIZER(GRAPH, NAME, INIT_FN, DEINIT_FN, PREREQS, DEPS)) + + +#define CONSTRUCT_DEPENDENCY_GRAPH(GRAPH, \ + INIT_FN0, \ + DEINIT_FN0, \ + INIT_FN1, \ + DEINIT_FN1, \ + INIT_FN2, \ + DEINIT_FN2, \ + INIT_FN3, \ + DEINIT_FN3, \ + INIT_FN4, \ + DEINIT_FN4, \ + INIT_FN5, \ + DEINIT_FN5, \ + INIT_FN6, \ + DEINIT_FN6, \ + INIT_FN7, \ + DEINIT_FN7, \ + INIT_FN8, \ + DEINIT_FN8) \ + do { \ + InitializerDependencyGraph& _graph_ = (GRAPH); \ + ASSERT_ADD_INITIALIZER( \ + _graph_, "n0", INIT_FN0, DEINIT_FN0, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER( \ + _graph_, "n1", INIT_FN1, DEINIT_FN1, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER( \ + _graph_, "n2", INIT_FN2, DEINIT_FN2, ("n0", "n1"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER( \ + _graph_, "n3", INIT_FN3, DEINIT_FN3, ("n0", "n2"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER( \ + _graph_, "n4", INIT_FN4, DEINIT_FN4, ("n2", "n1"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER( \ + _graph_, "n5", INIT_FN5, DEINIT_FN5, ("n3", "n4"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n6", INIT_FN6, DEINIT_FN6, ("n4"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n7", INIT_FN7, DEINIT_FN7, ("n3"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER( \ + _graph_, "n8", INIT_FN8, DEINIT_FN8, ("n5", "n6", "n7"), MONGO_NO_DEPENDENTS); \ } while (false) namespace mongo { namespace { -int globalCounts[9]; +enum State { + UNSET = 0, + INITIALIZED = 1, + DEINITIALIZED = 2, +}; + +State globalStates[9]; + +Status initNoop(InitializerContext*) { + return Status::OK(); +} + +Status deinitNoop(DeinitializerContext*) { + return Status::OK(); +} + +Status init0(InitializerContext*) { + globalStates[0] = INITIALIZED; + return Status::OK(); +} + +Status init1(InitializerContext*) { + globalStates[1] = INITIALIZED; + return Status::OK(); +} + +Status init2(InitializerContext*) { + if (globalStates[0] != INITIALIZED || globalStates[1] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(init2) one of 0 or 1 not already initialized"); + globalStates[2] = INITIALIZED; + return Status::OK(); +} + +Status init3(InitializerContext*) { + if (globalStates[0] != INITIALIZED || globalStates[2] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(init3) one of 0 or 2 not already initialized"); + globalStates[3] = INITIALIZED; + return Status::OK(); +} + +Status init4(InitializerContext*) { + if (globalStates[1] != INITIALIZED || globalStates[2] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(init4) one of 1 or 2 not already initialized"); + globalStates[4] = INITIALIZED; + return Status::OK(); +} -Status doNothing(InitializerContext*) { +Status init5(InitializerContext*) { + if (globalStates[3] != INITIALIZED || globalStates[4] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(init5) one of 3 or 4 not already initialized"); + globalStates[5] = INITIALIZED; return Status::OK(); } -Status set0(InitializerContext*) { - globalCounts[0] = 1; +Status init6(InitializerContext*) { + if (globalStates[4] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(init6) 4 not already initialized"); + globalStates[6] = INITIALIZED; return Status::OK(); } -Status set1(InitializerContext*) { - globalCounts[1] = 1; +Status init7(InitializerContext*) { + if (globalStates[3] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(init7) 3 not already initialized"); + globalStates[7] = INITIALIZED; return Status::OK(); } -Status set2(InitializerContext*) { - if (!globalCounts[0] || !globalCounts[1]) - return Status(ErrorCodes::UnknownError, "one of 0 or 1 not already set"); - globalCounts[2] = 1; +Status init8(InitializerContext*) { + if (globalStates[5] != INITIALIZED || globalStates[6] != INITIALIZED || + globalStates[7] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(init8) one of 5, 6, 7 not already initialized"); + globalStates[8] = INITIALIZED; return Status::OK(); } -Status set3(InitializerContext*) { - if (!globalCounts[0] || !globalCounts[2]) - return Status(ErrorCodes::UnknownError, "one of 0 or 2 not already set"); - globalCounts[3] = 1; +Status deinit8(DeinitializerContext*) { + if (globalStates[8] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit8) 8 not initialized"); + globalStates[8] = DEINITIALIZED; return Status::OK(); } -Status set4(InitializerContext*) { - if (!globalCounts[1] || !globalCounts[2]) - return Status(ErrorCodes::UnknownError, "one of 1 or 2 not already set"); - globalCounts[4] = 1; +Status deinit7(DeinitializerContext*) { + if (globalStates[7] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit7) 7 not initialized"); + if (globalStates[8] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit7) 8 not already deinitialized"); + globalStates[7] = DEINITIALIZED; return Status::OK(); } -Status set5(InitializerContext*) { - if (!globalCounts[3] || !globalCounts[4]) - return Status(ErrorCodes::UnknownError, "one of 3 or 4 not already set"); - globalCounts[5] = 1; +Status deinit6(DeinitializerContext*) { + if (globalStates[6] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit6) 6 not initialized"); + if (globalStates[8] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit6) 8 not already deinitialized"); + globalStates[6] = DEINITIALIZED; return Status::OK(); } -Status set6(InitializerContext*) { - if (!globalCounts[4]) - return Status(ErrorCodes::UnknownError, "4 not already set"); - globalCounts[6] = 1; +Status deinit5(DeinitializerContext*) { + if (globalStates[5] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit5) 5 not initialized"); + if (globalStates[8] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit5) 8 not already deinitialized"); + globalStates[5] = DEINITIALIZED; return Status::OK(); } -Status set7(InitializerContext*) { - if (!globalCounts[3]) - return Status(ErrorCodes::UnknownError, "3 not already set"); - globalCounts[7] = 1; +Status deinit4(DeinitializerContext*) { + if (globalStates[4] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit4) 4 not initialized"); + if (globalStates[5] != DEINITIALIZED || globalStates[6] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, + "(deinit4) one of 5 or 6 not already deinitialized"); + globalStates[4] = DEINITIALIZED; return Status::OK(); } -Status set8(InitializerContext*) { - if (!globalCounts[5] || !globalCounts[6] || !globalCounts[7]) - return Status(ErrorCodes::UnknownError, "one of 5, 6, 7 not already set"); - globalCounts[8] = 1; +Status deinit3(DeinitializerContext*) { + if (globalStates[3] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit3) 3 not initialized"); + if (globalStates[5] != DEINITIALIZED || globalStates[7] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, + "(deinit3) one of 5 or 7 not already deinitialized"); + globalStates[3] = DEINITIALIZED; + return Status::OK(); +} + +Status deinit2(DeinitializerContext*) { + if (globalStates[2] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit2) 2 not initialized"); + if (globalStates[3] != DEINITIALIZED || globalStates[4] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, + "(deinit2) one of 3 or 4 not already deinitialized"); + globalStates[2] = DEINITIALIZED; + return Status::OK(); +} + +Status deinit1(DeinitializerContext*) { + if (globalStates[1] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit1) 1 not initialized"); + if (globalStates[2] != DEINITIALIZED || globalStates[4] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, + "(deinit1) one of 2 or 4 not already deinitialized"); + globalStates[1] = DEINITIALIZED; + return Status::OK(); +} + +Status deinit0(DeinitializerContext*) { + if (globalStates[0] != INITIALIZED) + return Status(ErrorCodes::UnknownError, "(deinit0) 0 not initialized"); + if (globalStates[2] != DEINITIALIZED || globalStates[3] != DEINITIALIZED) + return Status(ErrorCodes::UnknownError, + "(deinit0) one of 2 or 3 not already deinitialized"); + globalStates[0] = DEINITIALIZED; return Status::OK(); } void clearCounts() { for (size_t i = 0; i < 9; ++i) - globalCounts[i] = 0; + globalStates[i] = UNSET; } -TEST(InitializerTest, SuccessfulInitialization) { +void constructNormalDependencyGraph(Initializer* initializer) { + CONSTRUCT_DEPENDENCY_GRAPH(initializer->getInitializerDependencyGraph(), + init0, + deinit0, + init1, + deinit1, + init2, + deinit2, + init3, + deinit3, + init4, + deinit4, + init5, + deinit5, + init6, + deinit6, + init7, + deinit7, + init8, + deinit8); +} + +TEST(InitializerTest, SuccessfulInitializationAndDeinitialization) { Initializer initializer; - CONSTRUCT_DEPENDENCY_GRAPH(initializer.getInitializerDependencyGraph(), - set0, - set1, - set2, - set3, - set4, - set5, - set6, - set7, - set8); + constructNormalDependencyGraph(&initializer); clearCounts(); + ASSERT_OK(initializer.executeInitializers(InitializerContext::ArgumentVector(), InitializerContext::EnvironmentMap())); + + for (int i = 0; i < 9; ++i) + ASSERT_EQUALS(INITIALIZED, globalStates[i]); + + ASSERT_OK(initializer.executeDeinitializers()); + for (int i = 0; i < 9; ++i) - ASSERT_EQUALS(1, globalCounts[i]); + ASSERT_EQUALS(DEINITIALIZED, globalStates[i]); } -TEST(InitializerTest, Step5Misimplemented) { +TEST(InitializerTest, Init5Misimplemented) { Initializer initializer; CONSTRUCT_DEPENDENCY_GRAPH(initializer.getInitializerDependencyGraph(), - set0, - set1, - set2, - set3, - set4, - doNothing, - set6, - set7, - set8); + init0, + deinitNoop, + init1, + deinitNoop, + init2, + deinitNoop, + init3, + deinitNoop, + init4, + deinitNoop, + initNoop, + deinitNoop, + init6, + deinitNoop, + init7, + deinitNoop, + init8, + deinitNoop); clearCounts(); + ASSERT_EQUALS(ErrorCodes::UnknownError, initializer.executeInitializers(InitializerContext::ArgumentVector(), InitializerContext::EnvironmentMap())); - ASSERT_EQUALS(1, globalCounts[0]); - ASSERT_EQUALS(1, globalCounts[1]); - ASSERT_EQUALS(1, globalCounts[2]); - ASSERT_EQUALS(1, globalCounts[3]); - ASSERT_EQUALS(1, globalCounts[4]); - ASSERT_EQUALS(0, globalCounts[8]); + + ASSERT_EQUALS(INITIALIZED, globalStates[0]); + ASSERT_EQUALS(INITIALIZED, globalStates[1]); + ASSERT_EQUALS(INITIALIZED, globalStates[2]); + ASSERT_EQUALS(INITIALIZED, globalStates[3]); + ASSERT_EQUALS(INITIALIZED, globalStates[4]); + ASSERT_EQUALS(UNSET, globalStates[5]); + ASSERT_EQUALS(INITIALIZED, globalStates[6]); + ASSERT_EQUALS(INITIALIZED, globalStates[7]); + ASSERT_EQUALS(UNSET, globalStates[8]); +} + +TEST(InitializerTest, Deinit2Misimplemented) { + Initializer initializer; + CONSTRUCT_DEPENDENCY_GRAPH(initializer.getInitializerDependencyGraph(), + init0, + deinit0, + init1, + deinit1, + init2, + deinitNoop, + init3, + deinit3, + init4, + deinit4, + init5, + deinit5, + init6, + deinit6, + init7, + deinit7, + init8, + deinit8); + clearCounts(); + + ASSERT_OK(initializer.executeInitializers(InitializerContext::ArgumentVector(), + InitializerContext::EnvironmentMap())); + + for (int i = 0; i < 9; ++i) + ASSERT_EQUALS(INITIALIZED, globalStates[i]); + + ASSERT_EQUALS(ErrorCodes::UnknownError, initializer.executeDeinitializers()); + + ASSERT_EQUALS(DEINITIALIZED, globalStates[8]); + ASSERT_EQUALS(DEINITIALIZED, globalStates[7]); + ASSERT_EQUALS(DEINITIALIZED, globalStates[6]); + ASSERT_EQUALS(DEINITIALIZED, globalStates[5]); + ASSERT_EQUALS(DEINITIALIZED, globalStates[4]); + ASSERT_EQUALS(DEINITIALIZED, globalStates[3]); + ASSERT_EQUALS(INITIALIZED, globalStates[2]); + ASSERT_EQUALS(INITIALIZED, globalStates[1]); + ASSERT_EQUALS(INITIALIZED, globalStates[0]); +} + +DEATH_TEST(InitializerTest, CannotAddInitializerAfterInitializing, "!frozen()") { + Initializer initializer; + constructNormalDependencyGraph(&initializer); + clearCounts(); + + ASSERT_OK(initializer.executeInitializers(InitializerContext::ArgumentVector(), + InitializerContext::EnvironmentMap())); + + for (int i = 0; i < 9; ++i) + ASSERT_EQUALS(INITIALIZED, globalStates[i]); + + ASSERT_ADD_INITIALIZER(initializer.getInitializerDependencyGraph(), + "test", + initNoop, + deinitNoop, + MONGO_NO_PREREQUISITES, + MONGO_NO_DEPENDENTS); +} + +DEATH_TEST(InitializerTest, CannotDoubleInitialize, "invalid initializer state transition") { + Initializer initializer; + constructNormalDependencyGraph(&initializer); + clearCounts(); + + ASSERT_OK(initializer.executeInitializers(InitializerContext::ArgumentVector(), + InitializerContext::EnvironmentMap())); + + for (int i = 0; i < 9; ++i) + ASSERT_EQUALS(INITIALIZED, globalStates[i]); + + initializer + .executeInitializers(InitializerContext::ArgumentVector(), + InitializerContext::EnvironmentMap()) + .ignore(); +} + +DEATH_TEST(InitializerTest, + CannotDeinitializeWithoutInitialize, + "invalid initializer state transition") { + Initializer initializer; + constructNormalDependencyGraph(&initializer); + clearCounts(); + + initializer.executeDeinitializers().ignore(); +} + +DEATH_TEST(InitializerTest, CannotDoubleDeinitialize, "invalid initializer state transition") { + Initializer initializer; + constructNormalDependencyGraph(&initializer); + clearCounts(); + + ASSERT_OK(initializer.executeInitializers(InitializerContext::ArgumentVector(), + InitializerContext::EnvironmentMap())); + + for (int i = 0; i < 9; ++i) + ASSERT_EQUALS(INITIALIZED, globalStates[i]); + + ASSERT_OK(initializer.executeDeinitializers()); + + for (int i = 0; i < 9; ++i) + ASSERT_EQUALS(DEINITIALIZED, globalStates[i]); + + initializer.executeDeinitializers().ignore(); } } // namespace |