Make order for deferred initializers deterministic

refs #8791
This commit is contained in:
Gunnar Beutner 2015-03-18 13:24:31 +01:00
parent ec2ae7101e
commit 3e08f2e0b5
16 changed files with 192 additions and 100 deletions

View File

@ -25,6 +25,7 @@
#include "base/logger.hpp"
#include "base/timer.hpp"
#include "base/utility.hpp"
#include "base/loader.hpp"
#include "base/exception.hpp"
#include "base/convert.hpp"
#include "base/scriptglobal.hpp"
@ -175,7 +176,7 @@ int Main(void)
LogSeverity logLevel = Logger::GetConsoleLogSeverity();
Logger::SetConsoleLogSeverity(LogWarning);
Utility::LoadExtensionLibrary("cli");
Loader::LoadExtensionLibrary("cli");
po::options_description visibleDesc("Global options");
@ -292,7 +293,7 @@ int Main(void)
if (vm.count("library")) {
BOOST_FOREACH(const String& libraryName, vm["library"].as<std::vector<std::string> >()) {
try {
(void) Utility::LoadExtensionLibrary(libraryName);
(void) Loader::LoadExtensionLibrary(libraryName);
} catch (const std::exception& ex) {
Log(LogCritical, "icinga-app")
<< "Could not load library \"" << libraryName << "\": " << DiagnosticInformation(ex);
@ -476,9 +477,9 @@ int Main(void)
Logger::SetConsoleLogSeverity(LogWarning);
if (vm.count("app"))
Utility::LoadExtensionLibrary(vm["app"].as<std::string>());
Loader::LoadExtensionLibrary(vm["app"].as<std::string>());
else
Utility::LoadExtensionLibrary("icinga");
Loader::LoadExtensionLibrary("icinga");
Logger::SetConsoleLogSeverity(logLevel);

View File

@ -25,7 +25,7 @@ mkclass_target(sysloglogger.ti sysloglogger.thpp)
set(base_SOURCES
application.cpp application-version.cpp application.thpp array.cpp array-script.cpp boolean.cpp boolean-script.cpp console.cpp context.cpp
convert.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp json-script.cpp logger.cpp logger.thpp math-script.cpp
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp json-script.cpp loader.cpp logger.cpp logger.thpp math-script.cpp
netstring.cpp networkstream.cpp number.cpp number-script.cpp object.cpp object-script.cpp primitivetype.cpp process.cpp
ringbuffer.cpp scriptframe.cpp function.cpp function-script.cpp functionwrapper.cpp scriptglobal.cpp
scriptutils.cpp serializer.cpp socket.cpp socketevents.cpp stacktrace.cpp

View File

@ -24,6 +24,7 @@
#include "base/exception.hpp"
#include "base/objectlock.hpp"
#include "base/utility.hpp"
#include "base/loader.hpp"
#include "base/debug.hpp"
#include "base/type.hpp"
#include "base/convert.hpp"
@ -134,7 +135,7 @@ void Application::InitializeBase(void)
}
#endif /* _WIN32 */
Utility::ExecuteDeferredInitializers();
Loader::ExecuteDeferredInitializers();
/* make sure the thread pool gets initialized */
GetTP();

View File

@ -18,13 +18,13 @@
******************************************************************************/
#include "base/initialize.hpp"
#include "base/utility.hpp"
#include "base/loader.hpp"
using namespace icinga;
bool icinga::InitializeOnceHelper(void (*func)(void))
bool icinga::InitializeOnceHelper(void (*func)(void), int priority)
{
Utility::AddDeferredInitializer(func);
Loader::AddDeferredInitializer(func, priority);
return true;
}

View File

@ -25,13 +25,17 @@
namespace icinga
{
I2_BASE_API bool InitializeOnceHelper(void (*func)(void));
I2_BASE_API bool InitializeOnceHelper(void (*func)(void), int priority = 0);
#define INITIALIZE_ONCE(func) \
namespace { namespace UNIQUE_NAME(io) { \
I2_EXPORT bool l_InitializeOnce(icinga::InitializeOnceHelper(func)); \
#define INITIALIZE_ONCE(func) \
namespace { namespace UNIQUE_NAME(io) { \
I2_EXPORT bool l_InitializeOnce(icinga::InitializeOnceHelper(func)); \
} }
#define INITIALIZE_ONCE_WITH_PRIORITY(func, priority) \
namespace { namespace UNIQUE_NAME(io) { \
I2_EXPORT bool l_InitializeOnce(icinga::InitializeOnceHelper(func, priority)); \
} }
}
#endif /* INITIALIZE_H */

90
lib/base/loader.cpp Normal file
View File

@ -0,0 +1,90 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "base/loader.hpp"
#include "base/logger.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
/**
* Loads the specified library.
*
* @param library The name of the library.
*/
void Loader::LoadExtensionLibrary(const String& library)
{
String path;
#if defined(_WIN32)
path = library + ".dll";
#elif defined(__APPLE__)
path = "lib" + library + ".dylib";
#else /* __APPLE__ */
path = "lib" + library + ".so";
#endif /* _WIN32 */
Log(LogInformation, "Utility")
<< "Loading library '" << path << "'";
#ifdef _WIN32
HMODULE hModule = LoadLibrary(path.CStr());
if (hModule == NULL) {
BOOST_THROW_EXCEPTION(win32_error()
<< boost::errinfo_api_function("LoadLibrary")
<< errinfo_win32_error(GetLastError())
<< boost::errinfo_file_name(path));
}
#else /* _WIN32 */
void *hModule = dlopen(path.CStr(), RTLD_NOW | RTLD_GLOBAL);
if (hModule == NULL) {
BOOST_THROW_EXCEPTION(std::runtime_error("Could not load library '" + path + "': " + dlerror()));
}
#endif /* _WIN32 */
ExecuteDeferredInitializers();
}
boost::thread_specific_ptr<std::priority_queue<DeferredInitializer> >& Loader::GetDeferredInitializers(void)
{
static boost::thread_specific_ptr<std::priority_queue<DeferredInitializer> > initializers;
return initializers;
}
void Loader::ExecuteDeferredInitializers(void)
{
if (!GetDeferredInitializers().get())
return;
while (!GetDeferredInitializers().get()->empty()) {
DeferredInitializer initializer = GetDeferredInitializers().get()->top();
GetDeferredInitializers().get()->pop();
initializer();
}
}
void Loader::AddDeferredInitializer(const boost::function<void(void)>& callback, int priority)
{
if (!GetDeferredInitializers().get())
GetDeferredInitializers().reset(new std::priority_queue<DeferredInitializer>());
GetDeferredInitializers().get()->push(DeferredInitializer(callback, priority));
}

76
lib/base/loader.hpp Normal file
View File

@ -0,0 +1,76 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef LOADER_H
#define LOADER_H
#include "base/i2-base.hpp"
#include "base/string.hpp"
#include <boost/thread/tss.hpp>
#include <boost/function.hpp>
#include <queue>
namespace icinga
{
struct DeferredInitializer
{
public:
DeferredInitializer(const boost::function<void (void)>& callback, int priority)
: m_Callback(callback), m_Priority(priority)
{ }
inline bool operator<(const DeferredInitializer& other) const
{
return m_Priority < other.m_Priority;
}
inline void operator()(void)
{
m_Callback();
}
private:
boost::function<void (void)> m_Callback;
int m_Priority;
};
/**
* Loader helper functions.
*
* @ingroup base
*/
class I2_BASE_API Loader
{
public:
static void LoadExtensionLibrary(const String& library);
static void AddDeferredInitializer(const boost::function<void(void)>& callback, int priority = 0);
static void ExecuteDeferredInitializers(void);
private:
Loader(void);
static boost::thread_specific_ptr<std::priority_queue<DeferredInitializer> >& GetDeferredInitializers(void);
};
}
#endif /* LOADER_H */

View File

@ -126,7 +126,7 @@ class TypeImpl
icinga::Type::Register(t); \
} \
\
INITIALIZE_ONCE(RegisterType ## type); \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterType ## type, 10); \
} } \
DEFINE_TYPE_INSTANCE(type)

View File

@ -291,72 +291,6 @@ void Utility::Sleep(double timeout)
#endif /* _WIN32 */
}
/**
* Loads the specified library.
*
* @param library The name of the library.
*/
void Utility::LoadExtensionLibrary(const String& library)
{
String path;
#if defined(_WIN32)
path = library + ".dll";
#elif defined(__APPLE__)
path = "lib" + library + ".dylib";
#else /* __APPLE__ */
path = "lib" + library + ".so";
#endif /* _WIN32 */
Log(LogInformation, "Utility")
<< "Loading library '" << path << "'";
#ifdef _WIN32
HMODULE hModule = LoadLibrary(path.CStr());
if (hModule == NULL) {
BOOST_THROW_EXCEPTION(win32_error()
<< boost::errinfo_api_function("LoadLibrary")
<< errinfo_win32_error(GetLastError())
<< boost::errinfo_file_name(path));
}
#else /* _WIN32 */
void *hModule = dlopen(path.CStr(), RTLD_NOW | RTLD_GLOBAL);
if (hModule == NULL) {
BOOST_THROW_EXCEPTION(std::runtime_error("Could not load library '" + path + "': " + dlerror()));
}
#endif /* _WIN32 */
ExecuteDeferredInitializers();
}
boost::thread_specific_ptr<std::vector<boost::function<void(void)> > >& Utility::GetDeferredInitializers(void)
{
static boost::thread_specific_ptr<std::vector<boost::function<void(void)> > > initializers;
return initializers;
}
void Utility::ExecuteDeferredInitializers(void)
{
if (!GetDeferredInitializers().get())
return;
BOOST_FOREACH(const boost::function<void(void)>& callback, *GetDeferredInitializers().get()) {
VERIFY(callback);
callback();
}
GetDeferredInitializers().reset();
}
void Utility::AddDeferredInitializer(const boost::function<void(void)>& callback)
{
if (!GetDeferredInitializers().get())
GetDeferredInitializers().reset(new std::vector<boost::function<void(void)> >());
GetDeferredInitializers().get()->push_back(callback);
}
/**
* Generates a new unique ID.
*

View File

@ -94,11 +94,6 @@ public:
static String FormatDateTime(const char *format, double ts);
static String FormatErrorNumber(int code);
static void LoadExtensionLibrary(const String& library);
static void AddDeferredInitializer(const boost::function<void(void)>& callback);
static void ExecuteDeferredInitializers(void);
#ifndef _WIN32
static void SetNonBlocking(int fd);
static void SetCloExec(int fd);
@ -139,8 +134,6 @@ private:
static boost::thread_specific_ptr<String> m_ThreadName;
static boost::thread_specific_ptr<unsigned int> m_RandSeed;
static boost::thread_specific_ptr<std::vector<boost::function<void(void)> > >& GetDeferredInitializers(void);
};
}

View File

@ -83,8 +83,6 @@ int NodeUpdateConfigCommand::Run(const boost::program_options::variables_map& vm
NodeUtility::PrintNodes(std::cout);
Utility::LoadExtensionLibrary("icinga");
/* cache all existing object configs only once and pass it to AddObject() */
std::vector<String> object_paths = RepositoryUtility::GetObjects();
/* cache all existing changes only once and pass it to AddObject() */

View File

@ -108,7 +108,6 @@ void RepositoryObjectCommand::InitParameters(boost::program_options::options_des
std::vector<String> RepositoryObjectCommand::GetPositionalSuggestions(const String& word) const
{
if (m_Command == RepositoryCommandAdd) {
Utility::LoadExtensionLibrary("icinga");
Type::Ptr ptype = Type::GetByName(m_Type);
ASSERT(ptype);
return GetFieldCompletionSuggestions(ptype, word);
@ -175,8 +174,6 @@ int RepositoryObjectCommand::Run(const boost::program_options::variables_map& vm
}
if (m_Command == RepositoryCommandAdd) {
Utility::LoadExtensionLibrary("icinga");
std::vector<String> object_paths = RepositoryUtility::GetObjects();
Array::Ptr changes = new Array();

View File

@ -445,10 +445,6 @@ bool TroubleshootCommand::PrintConf(InfoLog& log, const String& path)
bool TroubleshootCommand::CheckConfig(void)
{
/* Not loading the icinga library would make config validation fail.
* (Depending on the configuration and the speed of your machine.)
*/
Utility::LoadExtensionLibrary("icinga");
std::vector<std::string> configs;
configs.push_back(Application::GetSysconfDir() + "/icinga2/icinga2.conf");

View File

@ -21,6 +21,7 @@
#include "config/configitem.hpp"
#include "base/logger.hpp"
#include "base/utility.hpp"
#include "base/loader.hpp"
#include "base/context.hpp"
#include "base/exception.hpp"
#include <fstream>
@ -171,7 +172,7 @@ Expression *ConfigCompiler::HandleIncludeRecursive(const String& path, const Str
*/
void ConfigCompiler::HandleLibrary(const String& library)
{
Utility::LoadExtensionLibrary(library);
Loader::LoadExtensionLibrary(library);
}
void ConfigCompiler::CompileHelper(void)

View File

@ -34,7 +34,7 @@
delete expression; \
} \
\
INITIALIZE_ONCE(RegisterConfigFragment); \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterConfigFragment, 5); \
}
#endif /* CONFIGFRAGMENT_H */

View File

@ -27,6 +27,7 @@
#include "base/serializer.hpp"
#include "base/stdiostream.hpp"
#include "base/json.hpp"
#include "base/loader.hpp"
#include "cli/daemonutility.hpp"
#include <boost/test/unit_test.hpp>
#include <fstream>
@ -82,8 +83,8 @@ struct GlobalConfigFixture {
Application::DeclareSysconfDir("etc");
Application::DeclareLocalStateDir("var");
Utility::LoadExtensionLibrary("icinga");
Utility::LoadExtensionLibrary("methods"); //loaded by ITL
Loader::LoadExtensionLibrary("icinga");
Loader::LoadExtensionLibrary("methods"); //loaded by ITL
std::vector<std::string> configs;
configs.push_back(TestConfig);