-
Notifications
You must be signed in to change notification settings - Fork 577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ConfigItem::CommitNewItems(): pre-sort types by their load dependencies once #10003
Conversation
9726620
to
3ffa26a
Compare
3ffa26a
to
19aa779
Compare
19aa779
to
e1f5ab8
Compare
lib/base/type.hpp
Outdated
static std::vector<Type::Ptr> GetAllTypes(); | ||
static const std::vector<Ptr>& GetAllTypesSortedByLoadDependencies(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those two names suggest that the vectors should contain the same elements, just in a different order. However, there's some additional filtering:
Lines 49 to 51 in e1f5ab8
types.erase(std::remove_if(types.begin(), types.end(), [](auto& type) { | |
return !ConfigObject::TypeInstance->IsAssignableFrom(type); | |
}), types.end()); |
This should be reflected in the name.
lib/base/type.cpp
Outdated
static std::vector<Type::Ptr> l_SortedByLoadDependencies; | ||
static Atomic l_SortingByLoadDependenciesDone (false); | ||
|
||
typedef std::unordered_map<Type*, bool> Visited; // https://stackoverflow.com/a/8942986 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO a sentence would be more helpful than a link to a "try typedef" answer, where (at least to me), it's not that obvious why this applies here.1
Anyway, the way you use this type, I don't see a reason why this couldn't be a std::unordered_set<Type*>
instead, and checking if an element is contained instead of checking the bool value.
Footnotes
-
The question is about
SET_TYPE_NAME(std::map<int, int>, "TheMap")
which is way simpler what's happening here. There, I'd find it totally plausible if the C preprocessor would not care about the C++ template parameter syntax and treat<
and>
as separate operators. The lambda used here as a template argument contains multiple,
that aren't misinterpreted, so they are not a general problem as the C preprocessor understands some C syntax. The relevant part here is that even though{}
are also part of the C syntax, the C preprocessor doesn't seem to care too much about them. I haven't found a definite answer, but it looks like it cares only for string/character literals and()
. ↩
lib/base/type.cpp
Outdated
for (auto& type : types) { | ||
visited[type.get()] = false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also be just visited.clear()
, but I why is this done at all, you shouldn't have to revisit anything.
lib/base/type.cpp
Outdated
} | ||
|
||
bool& alreadyVisited (visited.at(type)); | ||
VERIFY(!alreadyVisited); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that VERIFY()
cycle detection in disguise? It would be more helpful if it actually said so as it would give you more of a clue of what happened if you manage to trigger it.
lib/base/type.cpp
Outdated
} | ||
|
||
VERIFY(sorted.size() == types.size()); | ||
VERIFY(sorted[0]->GetLoadDependencies().empty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a nice sanity check, but (obviously) just checks the first element. What about a test case for the whole order? Like iterate the whole vector and for each element, check that it's dependencies are placed at a lower index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about a test case for the whole order?
This is unnecessary IMHO, given that if the orders get messed up, you won't be able to start Icinga 2 anyway, so the startup phase actually takes care of such sanity checks and that function should just do what it is meant to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the startup phase actually takes care of such sanity checks
Does it check that explicitly? Or is it just that there's a good chance you'll get some errors then (but maybe it could just be broken in a subtle way)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not explicitly, but the type orders do matter when starting Icinga 2, and if you insist on introducing such explicit sanity checks, well then unittest it, but this function should not be bothered in checking the final result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about a test case
I was thinking of a unit test when writing that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about a test case
I was thinking of a unit test when writing that.
I thought you'd want to loop through the final result at the end and check their orders!
// Depth-first search | ||
std::unordered_set<Type*> unsorted; | ||
Visited visited; | ||
std::vector<Type::Ptr> sorted; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need to temporarily cache the sorted ones elsewhere than l_SortedByLoadDependencies
? It is guarded by VERIFY(l_SortingByLoadDependenciesDone.load());
so why can't you put the sorted ones directly in their final location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sorted var and the "atomic" std::swap() at the end (which I consider good practice) has no disadvantages IMAO.
if (unsorted.find(type) == unsorted.end()) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If, for whatever reason, you define in the *.ti
files that a type should be loaded after a random type that is not registered in the Types
namespace, then you can be sure that you will not get a Type*
if you load it with Type#GetLoadDependencies()
, but a nullptr
instead. So I would drop unsorted
entirely and perform a ...
alreadyVisited = true; | ||
|
||
for (auto dep : type->GetLoadDependencies()) { | ||
visit(dep); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... dep != nullptr
instead. Because if you put this in the host.ti
file, load_after awegqa24gtw34gtw34;
it will try to find this bogus type via GetByName("awegqa24gtw34gtw34").get()
, which obviously doesn't exist, and store a nullptr
in the load dependencies set that is returned when you call host->GetLoadDependencies()
.
types.erase(std::remove_if(types.begin(), types.end(), [](auto& type) { | ||
return !ConfigObject::TypeInstance->IsAssignableFrom(type); | ||
}), types.end()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also inline this check into the visit
callback, since you don't have to remove those unwanted types beforehand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I need to VERIFY(sorted.size() == types.size());
…es once to avoid complicated nested loops, iterating over the same types and checking dependencies over and over, skipping already completed ones.
e1f5ab8
to
a422419
Compare
As just discussed offline, this is being superseded by #10148, so please review that PR instead.
to avoid complicated nested loops, iterating over the same types and
checking dependencies over and over, skipping already completed ones.
closes #10148