summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndy Schwerin <schwerin@10gen.com>2012-09-17 12:19:26 -0400
committerAndy Schwerin <schwerin@10gen.com>2012-09-25 15:57:08 -0400
commit635aed09b0de72588422f1b864cca3810979375c (patch)
tree110365fc2105146c248025ef06e95db70ef3721b /src
parent1c9b2a7d236cce265db1b29a708959fe87ba56ca (diff)
downloadmongo-635aed09b0de72588422f1b864cca3810979375c.tar.gz
SERVER-5112 Introduce runGlobalInitializers(), macros and tools for global initializer declaration.
These utilities simplify adding global (process-wide) initializers that run at the beginning of main(). Also, run the global initializers in the main() of unittests.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/base/SConscript3
-rw-r--r--src/mongo/base/global_initializer.cpp36
-rw-r--r--src/mongo/base/global_initializer.h29
-rw-r--r--src/mongo/base/global_initializer_registerer.cpp43
-rw-r--r--src/mongo/base/global_initializer_registerer.h44
-rw-r--r--src/mongo/base/init.cpp23
-rw-r--r--src/mongo/base/init.h190
-rw-r--r--src/mongo/base/initializer.cpp38
-rw-r--r--src/mongo/base/initializer.h21
-rw-r--r--src/mongo/unittest/SConscript3
-rw-r--r--src/mongo/unittest/unittest_main.cpp4
11 files changed, 432 insertions, 2 deletions
diff --git a/src/mongo/base/SConscript b/src/mongo/base/SConscript
index 0c7d29abe9b..104d1a555cb 100644
--- a/src/mongo/base/SConscript
+++ b/src/mongo/base/SConscript
@@ -3,6 +3,9 @@
Import("env")
env.StaticLibrary('base', ['configuration_variable_manager.cpp',
+ 'global_initializer.cpp',
+ 'global_initializer_registerer.cpp',
+ 'init.cpp',
'initializer.cpp',
'initializer_context.cpp',
'initializer_dependency_graph.cpp',
diff --git a/src/mongo/base/global_initializer.cpp b/src/mongo/base/global_initializer.cpp
new file mode 100644
index 00000000000..4b1b8726bc2
--- /dev/null
+++ b/src/mongo/base/global_initializer.cpp
@@ -0,0 +1,36 @@
+/* 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/global_initializer.h"
+
+#include "mongo/base/initializer.h"
+
+namespace mongo {
+
+ Initializer& getGlobalInitializer() {
+ static Initializer theGlobalInitializer;
+ return theGlobalInitializer;
+ }
+
+namespace {
+
+ // Make sure that getGlobalInitializer() is called at least once before main(), and so at least
+ // once in a single-threaded context. Otherwise, static initialization inside
+ // getGlobalInitializer() won't be thread-safe.
+ Initializer* _theGlobalInitializer = &getGlobalInitializer();
+
+} // namespace
+
+} // namesapce mongo
diff --git a/src/mongo/base/global_initializer.h b/src/mongo/base/global_initializer.h
new file mode 100644
index 00000000000..4f5609c2d89
--- /dev/null
+++ b/src/mongo/base/global_initializer.h
@@ -0,0 +1,29 @@
+/* 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.
+ */
+
+#pragma once
+
+namespace mongo {
+ class Initializer;
+
+ /**
+ * Get the process-global initializer object.
+ *
+ * See mongo/base/initializer.h and mongo/base/init.h for information about process
+ * initialization in mongo applications.
+ */
+ Initializer& getGlobalInitializer();
+
+} // namespace mongo
diff --git a/src/mongo/base/global_initializer_registerer.cpp b/src/mongo/base/global_initializer_registerer.cpp
new file mode 100644
index 00000000000..43443554403
--- /dev/null
+++ b/src/mongo/base/global_initializer_registerer.cpp
@@ -0,0 +1,43 @@
+/* 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/global_initializer_registerer.h"
+
+#include <cstdlib>
+#include <iostream>
+
+#include "mongo/base/global_initializer.h"
+#include "mongo/base/initializer.h"
+
+namespace mongo {
+
+ GlobalInitializerRegisterer::GlobalInitializerRegisterer(
+ const std::string& name,
+ const InitializerFunction& fn,
+ const std::vector<std::string>& prerequisites,
+ const std::vector<std::string>& dependents) {
+
+ Status status = getGlobalInitializer().getInitializerDependencyGraph().addInitializer(
+ name, fn, prerequisites, dependents);
+
+
+ if (Status::OK() != status) {
+ std::cerr << "Attempt to add global initializer failed, status: "
+ << status << std::endl;
+ ::abort();
+ }
+ }
+
+} // namesapce mongo
diff --git a/src/mongo/base/global_initializer_registerer.h b/src/mongo/base/global_initializer_registerer.h
new file mode 100644
index 00000000000..e1aab9d3563
--- /dev/null
+++ b/src/mongo/base/global_initializer_registerer.h
@@ -0,0 +1,44 @@
+/* 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/initializer_function.h"
+#include "mongo/base/status.h"
+
+namespace mongo {
+
+ /**
+ * Type representing the act of registering a process-global intialization function.
+ *
+ * Create a module-global instance of this type to register a new initializer, to be run by a
+ * call to a variant of mongo::runGlobalInitializers(). See mongo/base/initializer.h,
+ * mongo/base/init.h and mongo/base/initializer_dependency_graph.h for details.
+ */
+ class GlobalInitializerRegisterer {
+ MONGO_DISALLOW_COPYING(GlobalInitializerRegisterer);
+
+ public:
+ GlobalInitializerRegisterer(const std::string& name,
+ const InitializerFunction& fn,
+ const std::vector<std::string>& prerequisites,
+ const std::vector<std::string>& dependents);
+ };
+
+} // namespace mongo
diff --git a/src/mongo/base/init.cpp b/src/mongo/base/init.cpp
new file mode 100644
index 00000000000..c03e63dd786
--- /dev/null
+++ b/src/mongo/base/init.cpp
@@ -0,0 +1,23 @@
+/* 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/init.h"
+
+MONGO_INITIALIZER_GROUP(default, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS)
+
+MONGO_INITIALIZER_GROUP(globalVariableConfigurationStarted, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS)
+MONGO_INITIALIZER_GROUP(globalVariablesDeclared, ("globalVariableConfigurationStarted"), MONGO_NO_DEPENDENTS)
+MONGO_INITIALIZER_GROUP(globalVariablesSet, ("globalVariablesDeclared"), MONGO_NO_DEPENDENTS)
+MONGO_INITIALIZER_GROUP(globalVariablesConfigured, ("globalVariablesDeclared"), ("default"))
diff --git a/src/mongo/base/init.h b/src/mongo/base/init.h
new file mode 100644
index 00000000000..7bf613fccbb
--- /dev/null
+++ b/src/mongo/base/init.h
@@ -0,0 +1,190 @@
+/* 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.
+ */
+
+/**
+ * Utility macros for declaring global initializers and configurable variables.
+ *
+ * Should NOT be included by other header files. Include only in source files.
+ *
+ * Initializers are arranged in an acyclic directed dependency graph. Declaring
+ * a cycle will lead to a runtime error.
+ *
+ * Initializer functions take a parameter of type ::mongo::InitializerContext*, and return
+ * a Status. Any status other than Status::OK() is considered a failure that will stop further
+ * intializer processing.
+ *
+ * Global configuration variables are declared and set using initializers and a few groups. All
+ * global configuration declarations have "globalVariableConfigurationStarted" as a prerequisite,
+ * and "globalVariablesDeclared" as a dependent. The easiest way for programs to then configure
+ * those values to non-default settings is to use the MONGO_CONFIG_VARIABLE_SETTER macro
+ * to declare exactly one function that has "globalVariablesDeclared" as a prerequisite and
+ * "globalVariablesSet" as a dependent.
+ *
+ * Initializers that wish to use configurable global variables must have "globalVariablesConfigured"
+ * as a direct or indirect prerequisite. The "default" prerequisite depends on
+ * "globalVariablesConfigured", so most initializer functions can safely use global configurable
+ * variables.
+ *
+ * Programmers may validate global variables after they are set using an initializer declared as
+ * MONGO_CONFIG_VARIABLE_VALIDATOR, which has "globalVariablesSet" as prerequisite and
+ * "globalVariablesConfigured" as dependent.
+ *
+ * In summary, the following partial order is provided:
+ * All MONGO_CONFIG_VARIABLE_REGISTER()s are evaluated before
+ * The MONGO_CONFIG_VARIABLE_SETTER is evaluated before
+ * All MONGO_CONFIG_VARIABLE_VALIDATORs are evaluated before
+ * Things dependent on "default" are evaluated.
+ */
+
+#pragma once
+
+#include "mongo/base/configuration_variable_manager.h"
+#include "mongo/base/initializer_context.h"
+#include "mongo/base/initializer_function.h"
+#include "mongo/base/global_initializer_registerer.h"
+#include "mongo/base/make_string_vector.h"
+#include "mongo/base/status.h"
+
+/**
+ * Convenience parameter representing an empty set of prerequisites for an initializer function.
+ */
+#define MONGO_NO_PREREQUISITES ()
+
+/**
+ * Convenience parameter representing an empty set of dependents of an initializer function.
+ */
+#define MONGO_NO_DEPENDENTS ()
+
+/**
+ * Convenience parameter representing the default set of dependents for initializer functions.
+ */
+#define MONGO_DEFAULT_PREREQUISITES ("default")
+
+/**
+ * Macro to define an initializer function named "NAME" with the default prerequisites, and
+ * no explicit dependents.
+ *
+ * See MONGO_INITIALIZER_GENERAL.
+ *
+ * Usage:
+ * MONGO_INITIALIZER(myModule)(::mongo::InitializerContext* context) {
+ * ...
+ * }
+ */
+#define MONGO_INITIALIZER(NAME) \
+ MONGO_INITIALIZER_WITH_PREREQUISITES(NAME, MONGO_DEFAULT_PREREQUISITES)
+
+/**
+ * Macro to define an initializer function named "NAME" that depends on the initializers
+ * specified in PREREQUISITES to have been completed, but names no explicit dependents.
+ *
+ * See MONGO_INITIALIZER_GENERAL.
+ *
+ * Usage:
+ * MONGO_INITIALIZER_WITH_PREREQUISITES(myGlobalStateChecker,
+ * ("globalStateInitialized", "stacktraces"))(
+ * ::mongo::InitializerContext* context) {
+ * }
+ */
+#define MONGO_INITIALIZER_WITH_PREREQUISITES(NAME, PREREQUISITES) \
+ MONGO_INITIALIZER_GENERAL(NAME, MONGO_DEFAULT_PREREQUISITES, MONGO_NO_DEPENDENTS)
+
+/**
+ * Macro to define an initializer that depends on PREREQUISITES and has DEPENDENTS as explicit
+ * dependents.
+ *
+ * NAME is any legitimate name for a C++ symbol.
+ * PREREQUISITES is a tuple of 0 or more string literals, i.e., ("a", "b", "c"), or ()
+ * DEPENDENTS is a tuple of 0 or more string literals.
+ *
+ * At run time, the full set of prerequisites for NAME will be computed as the union of the
+ * explicit PREREQUISITES and the set of all other mongo initializers that name NAME in their
+ * list of dependents.
+ *
+ * Usage:
+ * MONGO_INITIALIZER_GENERAL(myInitializer,
+ * ("myPrereq1", "myPrereq2", ...),
+ * ("myDependent1", "myDependent2", ...))(
+ * ::mongo::InitializerContext* context) {
+ * }
+ *
+ * TODO: May want to be able to name the initializer separately from the function name.
+ * A form that takes an existing function or that lets the programmer supply the name
+ * of the function to declare would be options.
+ */
+#define MONGO_INITIALIZER_GENERAL(NAME, PREREQUISITES, DEPENDENTS) \
+ ::mongo::Status _MONGO_INITIALIZER_FUNCTION_NAME(NAME)(::mongo::InitializerContext*); \
+ namespace { \
+ ::mongo::GlobalInitializerRegisterer _mongoInitializerRegisterer_##NAME( \
+ #NAME, \
+ _MONGO_INITIALIZER_FUNCTION_NAME(NAME), \
+ MONGO_MAKE_STRING_VECTOR PREREQUISITES, \
+ MONGO_MAKE_STRING_VECTOR DEPENDENTS); \
+ } \
+ ::mongo::Status _MONGO_INITIALIZER_FUNCTION_NAME(NAME)
+
+/**
+ * Macro to define an initializer group.
+ *
+ * An initializer group is an initializer that performs no actions. It is useful for organizing
+ * initialization steps into phases, such as "all global parameter declarations completed", "all
+ * global parameters initialized".
+ */
+#define MONGO_INITIALIZER_GROUP(NAME, PREREQUISITES, DEPENDENTS) \
+ MONGO_INITIALIZER_GENERAL(NAME, PREREQUISITES, DEPENDENTS)( \
+ ::mongo::InitializerContext*) { return ::mongo::Status::OK(); }
+
+
+/**
+ * Macro to register a configurable global variable.
+ *
+ * "NAME" is the string name through which the variable's storage may be accessed
+ * in the ConfigurationVariableManager supplied as part of the InitializerContext
+ * to global initializer functions. "STORAGE" is a pointer to the location in
+ * memory where the variable is stored, and "DEFAULT_VALUE" is the value to be
+ * assigned as the default, at registration time (once main has started). This
+ * allows DEFAULT_VALUE to be constructed after main() begins, so some options
+ * that are not available to static initializers may be available here.
+ */
+#define MONGO_CONFIG_VARIABLE_REGISTER(NAME, STORAGE, DEFAULT_VALUE) \
+ MONGO_INITIALIZER_GENERAL(cvr_##NAME, \
+ ("globalVariableConfigurationStarted"), \
+ ("globalVariablesDeclared"))( \
+ ::mongo::InitializerContext* context) { \
+ *(STORAGE) = (DEFAULT_VALUE); \
+ return GlobalInitializer::get().configurationVariables().registerVariable( \
+ #NAME, (STORAGE)); \
+ }
+
+/**
+ * Convenience macro for functions that validate already-set values of global
+ * variables. Run after the MONGO_CONFIG_VARIABLE_SETTER completes.
+ */
+#define MONGO_CONFIG_VARIABLE_VALIDATOR(NAME) \
+ MONGO_INITIALIZER_GENERAL(NAME, ("globalVariablesConfigured"), ("default"))
+
+/**
+ * Convenience macro for declaring the global variable setting function.
+ */
+#define MONGO_CONFIG_VARIABLE_SETTER \
+ MONGO_INITIALIZER_GENERAL(globalVariableSetter, \
+ ("globalVariablesDeclared"), \
+ ("globalVariablesSet"))
+
+/**
+ * Macro to produce a name for a mongo initializer function for an initializer operation
+ * named "NAME".
+ */
+#define _MONGO_INITIALIZER_FUNCTION_NAME(NAME) _mongoInitializerFunction_##NAME
diff --git a/src/mongo/base/initializer.cpp b/src/mongo/base/initializer.cpp
index f1921ba5637..4e1f36a83ce 100644
--- a/src/mongo/base/initializer.cpp
+++ b/src/mongo/base/initializer.cpp
@@ -15,6 +15,8 @@
#include "mongo/base/initializer.h"
+#include "mongo/base/global_initializer.h"
+
namespace mongo {
Initializer::Initializer() {}
@@ -44,4 +46,40 @@ namespace mongo {
return Status::OK();
}
+ Status runGlobalInitializers(const InitializerContext::ArgumentVector& args,
+ const InitializerContext::EnvironmentMap& env) {
+
+ return getGlobalInitializer().execute(args, env);
+ }
+
+ void runGlobalInitializersOrDie(const InitializerContext::ArgumentVector& args,
+ const InitializerContext::EnvironmentMap& env) {
+
+ Status status = runGlobalInitializers(args, env);
+ if (Status::OK() != status) {
+ std::cerr << "Failed global initialization: " << status << std::endl;
+ ::_exit(1);
+ }
+ }
+
+ void runGlobalInitializersOrDie(int argc, const char* const* argv, const char* const* envp) {
+ InitializerContext::ArgumentVector args(argc);
+ std::copy(argv, argv + argc, args.begin());
+
+ InitializerContext::EnvironmentMap env;
+
+ if (envp) {
+ for(; *envp; ++envp) {
+ const char* firstEqualSign = strchr(*envp, '=');
+ if (!firstEqualSign) {
+ std::cerr << "Failed global initialization: malformed environment block\n";
+ ::_exit(1);
+ }
+ env[std::string(*envp, firstEqualSign)] = std::string(firstEqualSign + 1);
+ }
+ }
+
+ runGlobalInitializersOrDie(args, env);
+ }
+
} // namespace mongo
diff --git a/src/mongo/base/initializer.h b/src/mongo/base/initializer.h
index 7d03cd6e40c..e54560eb72b 100644
--- a/src/mongo/base/initializer.h
+++ b/src/mongo/base/initializer.h
@@ -68,4 +68,25 @@ namespace mongo {
ConfigurationVariableManager _configVariables;
};
+ /**
+ * Run the global initializers.
+ *
+ * It's a programming error for this to fail, but if it does it will return a status other
+ * than Status::OK.
+ *
+ * This means that the few initializers that might want to terminate the program by failing
+ * should probably arrange to terminate the process themselves.
+ */
+ Status runGlobalInitializers(const InitializerContext::ArgumentVector& args,
+ const InitializerContext::EnvironmentMap& env);
+
+ /**
+ * Same as runGlobalInitializers(), except prints a brief message to std::cerr
+ * and terminates the process on failure.
+ */
+ void runGlobalInitializersOrDie(const InitializerContext::ArgumentVector& args,
+ const InitializerContext::EnvironmentMap& env);
+
+ void runGlobalInitializersOrDie(int argc, const char* const* argv, const char* const* envp);
+
} // namespace mongo
diff --git a/src/mongo/unittest/SConscript b/src/mongo/unittest/SConscript
index 12195e92fb4..bd9484ed970 100644
--- a/src/mongo/unittest/SConscript
+++ b/src/mongo/unittest/SConscript
@@ -6,7 +6,8 @@ env.StaticLibrary("unittest", ['unittest.cpp'],
LIBDEPS=['$BUILD_DIR/mongo/foundation'])
env.StaticLibrary("unittest_main", ['unittest_main.cpp'],
- LIBDEPS=['unittest'])
+ LIBDEPS=['unittest',
+ '$BUILD_DIR/mongo/base/base'])
env.StaticLibrary("unittest_crutch", ['crutch.cpp'])
diff --git a/src/mongo/unittest/unittest_main.cpp b/src/mongo/unittest/unittest_main.cpp
index 69113627564..0010835e3f9 100644
--- a/src/mongo/unittest/unittest_main.cpp
+++ b/src/mongo/unittest/unittest_main.cpp
@@ -3,9 +3,11 @@
#include <string>
#include <vector>
+#include "base/initializer.h"
#include "mongo/unittest/unittest.h"
-int main( int argc, char **argv ) {
+int main( int argc, char **argv, char **envp ) {
+ ::mongo::runGlobalInitializersOrDie(argc, argv, envp);
return ::mongo::unittest::Suite::run(std::vector<std::string>(), "", 1);
}