diff --git a/lib/base/initialize.hpp b/lib/base/initialize.hpp index adc995fe1b9..6c50b240875 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 1dcef39bfc9..390b37c8698 100644 --- a/lib/base/type.cpp +++ b/lib/base/type.cpp @@ -1,9 +1,14 @@ /* 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 using namespace icinga; @@ -32,6 +37,61 @@ INITIALIZE_ONCE_WITH_PRIORITY([]() { Type::Register(type); }, InitializePriority::RegisterTypeType); +static std::vector l_SortedByLoadDependencies; +static Atomic l_SortingByLoadDependenciesDone (false); + +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; + std::unordered_set visited; + std::vector sorted; + + for (auto type : types) { + unsorted.emplace(type.get()); + } + + std::function visit; + visit = ([&visit, &unsorted, &visited, &sorted](Type* type) { + if (unsorted.find(type) == unsorted.end()) { + return; + } + + VERIFY(visited.emplace(type).second); // Detect cycles in type dependencies + + for (auto dep : type->GetLoadDependencies()) { + visit(dep); + } + + unsorted.erase(type); + sorted.emplace_back(type); + }); + + while (!unsorted.empty()) { + visit(*unsorted.begin()); + } + + VERIFY(sorted.size() == types.size()); + + visited.clear(); + + for (auto& t : sorted) { + for (auto dep : t->GetLoadDependencies()) { + VERIFY(visited.find(dep) != visited.end()); + } + + VERIFY(visited.emplace(t.get()).second); + } + + std::swap(sorted, l_SortedByLoadDependencies); + l_SortingByLoadDependenciesDone.store(true); +}, InitializePriority::SortTypes); + String Type::ToString() const { return "type '" + GetName() + "'"; @@ -72,6 +132,12 @@ std::vector Type::GetAllTypes() return types; } +const std::vector& Type::GetAllConfigTypesSortedByLoadDependencies() +{ + 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 7b8d1cacb64..20f253dbe06 100644 --- a/lib/base/type.hpp +++ b/lib/base/type.hpp @@ -82,6 +82,7 @@ class Type : public Object static void Register(const Type::Ptr& type); static Type::Ptr GetByName(const String& name); static std::vector GetAllTypes(); + static const std::vector& GetAllConfigTypesSortedByLoadDependencies(); void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override; Value GetField(int id) const override;