Skip to content

Commit 467e8b1

Browse files
committed
Type: Simplify sort by load dependencies algorithm
1 parent 31f3aca commit 467e8b1

File tree

2 files changed

+33
-37
lines changed

2 files changed

+33
-37
lines changed

lib/base/type.cpp

+19-37
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
#include "base/scriptglobal.hpp"
88
#include "base/namespace.hpp"
99
#include "base/objectlock.hpp"
10-
#include <algorithm>
1110
#include <functional>
12-
#include <unordered_map>
1311

1412
using namespace icinga;
1513

@@ -41,53 +39,37 @@ INITIALIZE_ONCE_WITH_PRIORITY([]() {
4139
static std::vector<Type::Ptr> l_SortedByLoadDependencies;
4240
static Atomic l_SortingByLoadDependenciesDone (false);
4341

44-
typedef std::unordered_map<Type*, bool> Visited; // https://stackoverflow.com/a/8942986
45-
4642
INITIALIZE_ONCE_WITH_PRIORITY([] {
47-
auto types (Type::GetAllTypes());
48-
49-
types.erase(std::remove_if(types.begin(), types.end(), [](auto& type) {
50-
return !ConfigObject::TypeInstance->IsAssignableFrom(type);
51-
}), types.end());
52-
53-
// Depth-first search
54-
std::unordered_set<Type*> unsorted;
55-
Visited visited;
56-
std::vector<Type::Ptr> sorted;
57-
58-
for (auto type : types) {
59-
unsorted.emplace(type.get());
60-
}
43+
std::unordered_set<Type*> visited;
6144

62-
std::function<void(Type*)> visit ([&visit, &unsorted, &visited, &sorted](Type* type) {
63-
if (unsorted.find(type) == unsorted.end()) {
45+
std::function<void(Type*)> visit;
46+
// Please note that this callback does not detect any cyclic load dependencies,
47+
// instead, it relies on the "sort_by_load_after" unit test to fail.
48+
visit = ([&visit, &visited](Type* type) {
49+
if (visited.find(type) != visited.end()) {
6450
return;
6551
}
52+
visited.emplace(type);
6653

67-
bool& alreadyVisited (visited.at(type));
68-
VERIFY(!alreadyVisited);
69-
alreadyVisited = true;
70-
71-
for (auto dep : type->GetLoadDependencies()) {
72-
visit(dep);
54+
for (auto dependency : type->GetLoadDependencies()) {
55+
visit(dependency);
7356
}
7457

75-
unsorted.erase(type);
76-
sorted.emplace_back(type);
58+
// We have managed to reach the final/top node in this dependency graph,
59+
// so let's place them in reverse order to their final place.
60+
l_SortedByLoadDependencies.emplace_back(type);
7761
});
7862

79-
while (!unsorted.empty()) {
80-
for (auto& type : types) {
81-
visited[type.get()] = false;
63+
// Sort the types by their load_after dependencies in a Depth-First search manner.
64+
for (const Type::Ptr& type : Type::GetAllTypes()) {
65+
// Note that only those types that are assignable to the dynamic ConfigObject type can have "load_after"
66+
// dependencies, otherwise they are just some Icinga 2 primitive types such as Number, String, etc. and
67+
// we need to ignore them.
68+
if (ConfigObject::TypeInstance->IsAssignableFrom(type)) {
69+
visit(type.get());
8270
}
83-
84-
visit(*unsorted.begin());
8571
}
8672

87-
VERIFY(sorted.size() == types.size());
88-
VERIFY(sorted[0]->GetLoadDependencies().empty());
89-
90-
std::swap(sorted, l_SortedByLoadDependencies);
9173
l_SortingByLoadDependenciesDone.store(true);
9274
}, InitializePriority::SortTypes);
9375

lib/base/type.hpp

+14
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ class Type : public Object
8282
static void Register(const Type::Ptr& type);
8383
static Type::Ptr GetByName(const String& name);
8484
static std::vector<Type::Ptr> GetAllTypes();
85+
86+
/**
87+
* Returns a list of config types sorted by their "load_after" dependencies.
88+
*
89+
* All dependencies of a given type are listed at a lower index than that of the type itself. In other words,
90+
* if a `Service` type load depends on the `Host` and `ApiListener` types, the Host and ApiListener types are
91+
* guaranteed to appear first on the list. Nevertheless, the order of the Host and ApiListener types themselves
92+
* is arbitrary if the two types are not dependent.
93+
*
94+
* It should be noted that this method will fail fatally when used prior to the completion
95+
* of namespace initialization.
96+
*
97+
* @return std::vector<Type::Ptr>
98+
*/
8599
static const std::vector<Ptr>& GetConfigTypesSortedByLoadDependencies();
86100

87101
void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override;

0 commit comments

Comments
 (0)