mirror of https://github.com/Icinga/icinga2.git
Type: Simplify sort by load dependencies algorithm
This commit is contained in:
parent
31f3acaa13
commit
467e8b18e7
|
@ -7,9 +7,7 @@
|
||||||
#include "base/scriptglobal.hpp"
|
#include "base/scriptglobal.hpp"
|
||||||
#include "base/namespace.hpp"
|
#include "base/namespace.hpp"
|
||||||
#include "base/objectlock.hpp"
|
#include "base/objectlock.hpp"
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
|
|
||||||
|
@ -41,53 +39,37 @@ INITIALIZE_ONCE_WITH_PRIORITY([]() {
|
||||||
static std::vector<Type::Ptr> l_SortedByLoadDependencies;
|
static std::vector<Type::Ptr> l_SortedByLoadDependencies;
|
||||||
static Atomic l_SortingByLoadDependenciesDone (false);
|
static Atomic l_SortingByLoadDependenciesDone (false);
|
||||||
|
|
||||||
typedef std::unordered_map<Type*, bool> Visited; // https://stackoverflow.com/a/8942986
|
|
||||||
|
|
||||||
INITIALIZE_ONCE_WITH_PRIORITY([] {
|
INITIALIZE_ONCE_WITH_PRIORITY([] {
|
||||||
auto types (Type::GetAllTypes());
|
std::unordered_set<Type*> visited;
|
||||||
|
|
||||||
types.erase(std::remove_if(types.begin(), types.end(), [](auto& type) {
|
std::function<void(Type*)> visit;
|
||||||
return !ConfigObject::TypeInstance->IsAssignableFrom(type);
|
// Please note that this callback does not detect any cyclic load dependencies,
|
||||||
}), types.end());
|
// instead, it relies on the "sort_by_load_after" unit test to fail.
|
||||||
|
visit = ([&visit, &visited](Type* type) {
|
||||||
// Depth-first search
|
if (visited.find(type) != visited.end()) {
|
||||||
std::unordered_set<Type*> unsorted;
|
|
||||||
Visited visited;
|
|
||||||
std::vector<Type::Ptr> sorted;
|
|
||||||
|
|
||||||
for (auto type : types) {
|
|
||||||
unsorted.emplace(type.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::function<void(Type*)> visit ([&visit, &unsorted, &visited, &sorted](Type* type) {
|
|
||||||
if (unsorted.find(type) == unsorted.end()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
visited.emplace(type);
|
||||||
|
|
||||||
bool& alreadyVisited (visited.at(type));
|
for (auto dependency : type->GetLoadDependencies()) {
|
||||||
VERIFY(!alreadyVisited);
|
visit(dependency);
|
||||||
alreadyVisited = true;
|
|
||||||
|
|
||||||
for (auto dep : type->GetLoadDependencies()) {
|
|
||||||
visit(dep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsorted.erase(type);
|
// We have managed to reach the final/top node in this dependency graph,
|
||||||
sorted.emplace_back(type);
|
// so let's place them in reverse order to their final place.
|
||||||
|
l_SortedByLoadDependencies.emplace_back(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
while (!unsorted.empty()) {
|
// Sort the types by their load_after dependencies in a Depth-First search manner.
|
||||||
for (auto& type : types) {
|
for (const Type::Ptr& type : Type::GetAllTypes()) {
|
||||||
visited[type.get()] = false;
|
// Note that only those types that are assignable to the dynamic ConfigObject type can have "load_after"
|
||||||
|
// dependencies, otherwise they are just some Icinga 2 primitive types such as Number, String, etc. and
|
||||||
|
// we need to ignore them.
|
||||||
|
if (ConfigObject::TypeInstance->IsAssignableFrom(type)) {
|
||||||
|
visit(type.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(*unsorted.begin());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VERIFY(sorted.size() == types.size());
|
|
||||||
VERIFY(sorted[0]->GetLoadDependencies().empty());
|
|
||||||
|
|
||||||
std::swap(sorted, l_SortedByLoadDependencies);
|
|
||||||
l_SortingByLoadDependenciesDone.store(true);
|
l_SortingByLoadDependenciesDone.store(true);
|
||||||
}, InitializePriority::SortTypes);
|
}, InitializePriority::SortTypes);
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,20 @@ public:
|
||||||
static void Register(const Type::Ptr& type);
|
static void Register(const Type::Ptr& type);
|
||||||
static Type::Ptr GetByName(const String& name);
|
static Type::Ptr GetByName(const String& name);
|
||||||
static std::vector<Type::Ptr> GetAllTypes();
|
static std::vector<Type::Ptr> GetAllTypes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of config types sorted by their "load_after" dependencies.
|
||||||
|
*
|
||||||
|
* All dependencies of a given type are listed at a lower index than that of the type itself. In other words,
|
||||||
|
* if a `Service` type load depends on the `Host` and `ApiListener` types, the Host and ApiListener types are
|
||||||
|
* guaranteed to appear first on the list. Nevertheless, the order of the Host and ApiListener types themselves
|
||||||
|
* is arbitrary if the two types are not dependent.
|
||||||
|
*
|
||||||
|
* It should be noted that this method will fail fatally when used prior to the completion
|
||||||
|
* of namespace initialization.
|
||||||
|
*
|
||||||
|
* @return std::vector<Type::Ptr>
|
||||||
|
*/
|
||||||
static const std::vector<Ptr>& GetConfigTypesSortedByLoadDependencies();
|
static const std::vector<Ptr>& GetConfigTypesSortedByLoadDependencies();
|
||||||
|
|
||||||
void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override;
|
void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override;
|
||||||
|
|
Loading…
Reference in New Issue