diff options
author | Andy Schwerin <schwerin@10gen.com> | 2012-09-17 12:19:26 -0400 |
---|---|---|
committer | Andy Schwerin <schwerin@10gen.com> | 2012-09-25 15:57:08 -0400 |
commit | 635aed09b0de72588422f1b864cca3810979375c (patch) | |
tree | 110365fc2105146c248025ef06e95db70ef3721b /src/mongo | |
parent | 1c9b2a7d236cce265db1b29a708959fe87ba56ca (diff) | |
download | mongo-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/mongo')
-rw-r--r-- | src/mongo/base/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/base/global_initializer.cpp | 36 | ||||
-rw-r--r-- | src/mongo/base/global_initializer.h | 29 | ||||
-rw-r--r-- | src/mongo/base/global_initializer_registerer.cpp | 43 | ||||
-rw-r--r-- | src/mongo/base/global_initializer_registerer.h | 44 | ||||
-rw-r--r-- | src/mongo/base/init.cpp | 23 | ||||
-rw-r--r-- | src/mongo/base/init.h | 190 | ||||
-rw-r--r-- | src/mongo/base/initializer.cpp | 38 | ||||
-rw-r--r-- | src/mongo/base/initializer.h | 21 | ||||
-rw-r--r-- | src/mongo/unittest/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/unittest/unittest_main.cpp | 4 |
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); } |