summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Pulo <kevin.pulo@mongodb.com>2020-03-16 16:43:39 +1100
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-05-18 08:08:44 +0000
commit9f6af24864286343052497e827f06cba350c3c3e (patch)
tree1c33f84c8b8863604796a3d28bc107c3c46f0f24
parentd0e6f87351312a299a17f6d63e3f2f4db834ae1f (diff)
downloadmongo-9f6af24864286343052497e827f06cba350c3c3e.tar.gz
SERVER-46881 Forbid adding nodes to initializer graph after starting initializer execution
-rw-r--r--src/mongo/base/initializer.cpp52
-rw-r--r--src/mongo/base/initializer.h9
-rw-r--r--src/mongo/base/initializer_dependency_graph.cpp2
-rw-r--r--src/mongo/base/initializer_dependency_graph.h40
-rw-r--r--src/mongo/base/initializer_dependency_graph_test.cpp38
-rw-r--r--src/mongo/base/initializer_test.cpp426
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