diff --git a/lib/base/initialize.hpp b/lib/base/initialize.hpp index adc995fe1..6c50b2408 100644 --- a/lib/base/initialize.hpp +++ b/lib/base/initialize.hpp @@ -23,6 +23,7 @@ enum class InitializePriority { RegisterBuiltinTypes, RegisterFunctions, RegisterTypes, + SortTypes, EvaluateConfigFragments, Default, FreezeNamespaces, diff --git a/lib/base/type.cpp b/lib/base/type.cpp index 1dcef39bf..70dd6333e 100644 --- a/lib/base/type.cpp +++ b/lib/base/type.cpp @@ -1,9 +1,15 @@ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/type.hpp" +#include "base/atomic.hpp" +#include "base/configobject.hpp" +#include "base/debug.hpp" #include "base/scriptglobal.hpp" #include "base/namespace.hpp" #include "base/objectlock.hpp" +#include +#include +#include using namespace icinga; @@ -32,6 +38,59 @@ INITIALIZE_ONCE_WITH_PRIORITY([]() { Type::Register(type); }, InitializePriority::RegisterTypeType); +static std::vector l_SortedByLoadDependencies; +static Atomic l_SortingByLoadDependenciesDone (false); + +typedef std::unordered_map Visited; // https://stackoverflow.com/a/8942986 + +INITIALIZE_ONCE_WITH_PRIORITY([] { + auto types (Type::GetAllTypes()); + + types.erase(std::remove_if(types.begin(), types.end(), [](auto& type) { + return !ConfigObject::TypeInstance->IsAssignableFrom(type); + }), types.end()); + + // Depth-first search + std::unordered_set unsorted; + Visited visited; + std::vector sorted; + + for (auto type : types) { + unsorted.emplace(type.get()); + } + + std::function visit ([&visit, &unsorted, &visited, &sorted](Type* type) { + if (unsorted.find(type) == unsorted.end()) { + return; + } + + bool& alreadyVisited (visited.at(type)); + VERIFY(!alreadyVisited); + alreadyVisited = true; + + for (auto dep : type->GetLoadDependencies()) { + visit(dep); + } + + unsorted.erase(type); + sorted.emplace_back(type); + }); + + while (!unsorted.empty()) { + for (auto& type : types) { + visited[type.get()] = false; + } + + visit(*unsorted.begin()); + } + + VERIFY(sorted.size() == types.size()); + VERIFY(sorted[0]->GetLoadDependencies().empty()); + + std::swap(sorted, l_SortedByLoadDependencies); + l_SortingByLoadDependenciesDone.store(true); +}, InitializePriority::SortTypes); + String Type::ToString() const { return "type '" + GetName() + "'"; @@ -72,6 +131,12 @@ std::vector Type::GetAllTypes() return types; } +const std::vector& Type::GetConfigTypesSortedByLoadDependencies() +{ + VERIFY(l_SortingByLoadDependenciesDone.load()); + return l_SortedByLoadDependencies; +} + String Type::GetPluralName() const { String name = GetName(); diff --git a/lib/base/type.hpp b/lib/base/type.hpp index 7b8d1cacb..fa78f858e 100644 --- a/lib/base/type.hpp +++ b/lib/base/type.hpp @@ -82,6 +82,7 @@ public: static void Register(const Type::Ptr& type); static Type::Ptr GetByName(const String& name); static std::vector GetAllTypes(); + static const std::vector& GetConfigTypesSortedByLoadDependencies(); void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override; Value GetField(int id) const override;