mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-30 00:54:30 +02:00
DependencyGroup: don't change the keys of m_Members after construction
This prevents the use of DependencyGroup for storing the dependencies during the early registration (m_DependencyGroupsPushedToRegistry = false), m_PendingDependencies is introduced as a replacement to store the dependencies at that time.
This commit is contained in:
parent
945a79e37f
commit
693d094ebc
@ -26,15 +26,12 @@ static constexpr int l_MaxDependencyRecursionLevel(256);
|
|||||||
void Checkable::PushDependencyGroupsToRegistry()
|
void Checkable::PushDependencyGroupsToRegistry()
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_DependencyMutex);
|
std::lock_guard lock(m_DependencyMutex);
|
||||||
if (!m_DependencyGroupsPushedToRegistry) {
|
if (m_PendingDependencies != nullptr) {
|
||||||
m_DependencyGroupsPushedToRegistry = true;
|
for (const auto& [key, dependencies] : *m_PendingDependencies) {
|
||||||
|
String redundancyGroup = std::holds_alternative<String>(key) ? std::get<String>(key) : "";
|
||||||
decltype(m_DependencyGroups) dependencyGroups;
|
m_DependencyGroups.emplace(key, DependencyGroup::Register(new DependencyGroup(redundancyGroup, dependencies)));
|
||||||
m_DependencyGroups.swap(dependencyGroups);
|
|
||||||
|
|
||||||
for (auto& [dependencyGroupKey, dependencyGroup] : dependencyGroups) {
|
|
||||||
m_DependencyGroups.emplace(dependencyGroupKey, DependencyGroup::Register(dependencyGroup));
|
|
||||||
}
|
}
|
||||||
|
m_PendingDependencies.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,12 +75,8 @@ void Checkable::AddDependency(const Dependency::Ptr& dependency)
|
|||||||
std::unique_lock lock(m_DependencyMutex);
|
std::unique_lock lock(m_DependencyMutex);
|
||||||
|
|
||||||
auto dependencyGroupKey(GetDependencyGroupKey(dependency));
|
auto dependencyGroupKey(GetDependencyGroupKey(dependency));
|
||||||
if (!m_DependencyGroupsPushedToRegistry) {
|
if (m_PendingDependencies != nullptr) {
|
||||||
auto& dependencyGroup = m_DependencyGroups[dependencyGroupKey];
|
(*m_PendingDependencies)[dependencyGroupKey].emplace(dependency);
|
||||||
if (!dependencyGroup) {
|
|
||||||
dependencyGroup = new DependencyGroup(dependency->GetRedundancyGroup());
|
|
||||||
}
|
|
||||||
dependencyGroup->AddDependency(dependency);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,10 +144,17 @@ void Checkable::RemoveDependency(const Dependency::Ptr& dependency, bool runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Dependency::Ptr> Checkable::GetDependencies() const
|
std::vector<Dependency::Ptr> Checkable::GetDependencies(bool includePending) const
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_DependencyMutex);
|
std::unique_lock<std::mutex> lock(m_DependencyMutex);
|
||||||
std::vector<Dependency::Ptr> dependencies;
|
std::vector<Dependency::Ptr> dependencies;
|
||||||
|
|
||||||
|
if (includePending && m_PendingDependencies != nullptr) {
|
||||||
|
for (const auto& [group, groupDeps] : *m_PendingDependencies) {
|
||||||
|
dependencies.insert(dependencies.end(), groupDeps.begin(), groupDeps.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& [_, dependencyGroup] : m_DependencyGroups) {
|
for (const auto& [_, dependencyGroup] : m_DependencyGroups) {
|
||||||
auto tmpDependencies(dependencyGroup->GetDependenciesForChild(this));
|
auto tmpDependencies(dependencyGroup->GetDependenciesForChild(this));
|
||||||
dependencies.insert(dependencies.end(), tmpDependencies.begin(), tmpDependencies.end());
|
dependencies.insert(dependencies.end(), tmpDependencies.begin(), tmpDependencies.end());
|
||||||
|
@ -190,7 +190,7 @@ public:
|
|||||||
std::vector<intrusive_ptr<DependencyGroup>> GetDependencyGroups() const;
|
std::vector<intrusive_ptr<DependencyGroup>> GetDependencyGroups() const;
|
||||||
void AddDependency(const intrusive_ptr<Dependency>& dependency);
|
void AddDependency(const intrusive_ptr<Dependency>& dependency);
|
||||||
void RemoveDependency(const intrusive_ptr<Dependency>& dependency, bool runtimeRemoved = false);
|
void RemoveDependency(const intrusive_ptr<Dependency>& dependency, bool runtimeRemoved = false);
|
||||||
std::vector<intrusive_ptr<Dependency> > GetDependencies() const;
|
std::vector<intrusive_ptr<Dependency> > GetDependencies(bool includePending = false) const;
|
||||||
bool HasAnyDependencies() const;
|
bool HasAnyDependencies() const;
|
||||||
|
|
||||||
void AddReverseDependency(const intrusive_ptr<Dependency>& dep);
|
void AddReverseDependency(const intrusive_ptr<Dependency>& dep);
|
||||||
@ -251,9 +251,19 @@ private:
|
|||||||
|
|
||||||
/* Dependencies */
|
/* Dependencies */
|
||||||
mutable std::mutex m_DependencyMutex;
|
mutable std::mutex m_DependencyMutex;
|
||||||
bool m_DependencyGroupsPushedToRegistry{false};
|
|
||||||
std::map<std::variant<Checkable*, String>, intrusive_ptr<DependencyGroup>> m_DependencyGroups;
|
std::map<std::variant<Checkable*, String>, intrusive_ptr<DependencyGroup>> m_DependencyGroups;
|
||||||
std::set<intrusive_ptr<Dependency> > m_ReverseDependencies;
|
std::set<intrusive_ptr<Dependency> > m_ReverseDependencies;
|
||||||
|
/**
|
||||||
|
* Registering a checkable to its parent DependencyGroups is delayed during config loading until all dependencies
|
||||||
|
* were registered on the checkable. m_PendingDependencies is used to temporarily store the dependencies until then.
|
||||||
|
* It is a pointer type for two reasons:
|
||||||
|
* 1. The field is no longer needed after the DependencyGroups were registered, having it as a pointer reduces the
|
||||||
|
* overhead from sizeof(std::map<>) to sizeof(std::map<>*).
|
||||||
|
* 2. It allows the field to also be used as a flag: the delayed group registration is only done until it is reset
|
||||||
|
* to nullptr.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<std::map<std::variant<Checkable*, String>, std::set<intrusive_ptr<Dependency>>>>
|
||||||
|
m_PendingDependencies {std::make_unique<decltype(m_PendingDependencies)::element_type>()};
|
||||||
|
|
||||||
void GetAllChildrenInternal(std::set<Checkable::Ptr>& seenChildren, int level = 0) const;
|
void GetAllChildrenInternal(std::set<Checkable::Ptr>& seenChildren, int level = 0) const;
|
||||||
|
|
||||||
|
@ -46,17 +46,18 @@ std::pair<std::set<Dependency::Ptr>, bool> DependencyGroup::Unregister(const Dep
|
|||||||
{
|
{
|
||||||
std::lock_guard lock(m_RegistryMutex);
|
std::lock_guard lock(m_RegistryMutex);
|
||||||
if (auto it(m_Registry.find(dependencyGroup)); it != m_Registry.end()) {
|
if (auto it(m_Registry.find(dependencyGroup)); it != m_Registry.end()) {
|
||||||
auto existingGroup(*it);
|
auto& existingGroup(*it);
|
||||||
auto dependencies(existingGroup->GetDependenciesForChild(child.get()));
|
auto dependencies(existingGroup->GetDependenciesForChild(child.get()));
|
||||||
|
|
||||||
for (const auto& dependency : dependencies) {
|
for (const auto& dependency : dependencies) {
|
||||||
existingGroup->RemoveDependency(dependency);
|
existingGroup->RemoveDependency(dependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingGroup->IsEmpty()) {
|
bool remove = !existingGroup->HasChildren();
|
||||||
|
if (remove) {
|
||||||
m_Registry.erase(it);
|
m_Registry.erase(it);
|
||||||
}
|
}
|
||||||
return {{dependencies.begin(), dependencies.end()}, existingGroup->IsEmpty()};
|
return {{dependencies.begin(), dependencies.end()}, remove};
|
||||||
}
|
}
|
||||||
return {{}, false};
|
return {{}, false};
|
||||||
}
|
}
|
||||||
@ -72,14 +73,11 @@ size_t DependencyGroup::GetRegistrySize()
|
|||||||
return m_Registry.size();
|
return m_Registry.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
DependencyGroup::DependencyGroup(String name): m_RedundancyGroupName(std::move(name))
|
DependencyGroup::DependencyGroup(String name, const std::set<Dependency::Ptr>& dependencies)
|
||||||
{
|
: m_RedundancyGroupName(std::move(name))
|
||||||
}
|
|
||||||
|
|
||||||
DependencyGroup::DependencyGroup(String name, const std::set<Dependency::Ptr>& dependencies): m_RedundancyGroupName(std::move(name))
|
|
||||||
{
|
{
|
||||||
for (const auto& dependency : dependencies) {
|
for (const auto& dependency : dependencies) {
|
||||||
AddDependency(dependency);
|
m_Members[MakeCompositeKeyFor(dependency)].emplace(dependency->GetChild().get(), dependency.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,14 +101,14 @@ DependencyGroup::CompositeKeyType DependencyGroup::MakeCompositeKeyFor(const Dep
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current dependency group is empty.
|
* Check if the current dependency has any children.
|
||||||
*
|
*
|
||||||
* @return bool - Returns true if the current dependency group has no members, otherwise false.
|
* @return bool - Returns true if the current dependency group has children, otherwise false.
|
||||||
*/
|
*/
|
||||||
bool DependencyGroup::IsEmpty() const
|
bool DependencyGroup::HasChildren() const
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_Mutex);
|
std::lock_guard lock(m_Mutex);
|
||||||
return m_Members.empty();
|
return std::any_of(m_Members.begin(), m_Members.end(), [](const auto& pair) { return !pair.second.empty(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,7 +140,6 @@ void DependencyGroup::LoadParents(std::set<Checkable::Ptr>& parents) const
|
|||||||
{
|
{
|
||||||
std::lock_guard lock(m_Mutex);
|
std::lock_guard lock(m_Mutex);
|
||||||
for (auto& [compositeKey, children] : m_Members) {
|
for (auto& [compositeKey, children] : m_Members) {
|
||||||
ASSERT(!children.empty()); // We should never have an empty map for any given key at any given time.
|
|
||||||
parents.insert(std::get<0>(compositeKey));
|
parents.insert(std::get<0>(compositeKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,11 +171,12 @@ void DependencyGroup::AddDependency(const Dependency::Ptr& dependency)
|
|||||||
{
|
{
|
||||||
std::lock_guard lock(m_Mutex);
|
std::lock_guard lock(m_Mutex);
|
||||||
auto compositeKey(MakeCompositeKeyFor(dependency));
|
auto compositeKey(MakeCompositeKeyFor(dependency));
|
||||||
if (auto it(m_Members.find(compositeKey)); it != m_Members.end()) {
|
auto it = m_Members.find(compositeKey);
|
||||||
|
|
||||||
|
// The dependency must be compatible with the group, i.e. its parent config must be known in the group already.
|
||||||
|
VERIFY(it != m_Members.end());
|
||||||
|
|
||||||
it->second.emplace(dependency->GetChild().get(), dependency.get());
|
it->second.emplace(dependency->GetChild().get(), dependency.get());
|
||||||
} else {
|
|
||||||
m_Members.emplace(compositeKey, MemberValueType{{dependency->GetChild().get(), dependency.get()}});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,10 +194,6 @@ void DependencyGroup::RemoveDependency(const Dependency::Ptr& dependency)
|
|||||||
// This will also remove the child Checkable from the multimap container
|
// This will also remove the child Checkable from the multimap container
|
||||||
// entirely if this was the last child of it.
|
// entirely if this was the last child of it.
|
||||||
it->second.erase(childrenIt);
|
it->second.erase(childrenIt);
|
||||||
// If the composite key has no more children left, we can remove it entirely as well.
|
|
||||||
if (it->second.empty()) {
|
|
||||||
m_Members.erase(it);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly configured dependency objects
|
// Explicitly configured dependency objects
|
||||||
for (const auto& dep : checkable->GetDependencies()) {
|
for (const auto& dep : checkable->GetDependencies(/* includePending = */ true)) {
|
||||||
m_Stack.emplace_back(dep);
|
m_Stack.emplace_back(dep);
|
||||||
AssertNoCycle(dep->GetParent());
|
AssertNoCycle(dep->GetParent());
|
||||||
m_Stack.pop_back();
|
m_Stack.pop_back();
|
||||||
|
@ -132,7 +132,6 @@ public:
|
|||||||
using MemberValueType = std::unordered_multimap<const Checkable*, Dependency*>;
|
using MemberValueType = std::unordered_multimap<const Checkable*, Dependency*>;
|
||||||
using MembersMap = std::map<CompositeKeyType, MemberValueType>;
|
using MembersMap = std::map<CompositeKeyType, MemberValueType>;
|
||||||
|
|
||||||
explicit DependencyGroup(String name);
|
|
||||||
DependencyGroup(String name, const std::set<Dependency::Ptr>& dependencies);
|
DependencyGroup(String name, const std::set<Dependency::Ptr>& dependencies);
|
||||||
|
|
||||||
static DependencyGroup::Ptr Register(const DependencyGroup::Ptr& dependencyGroup);
|
static DependencyGroup::Ptr Register(const DependencyGroup::Ptr& dependencyGroup);
|
||||||
@ -151,7 +150,7 @@ public:
|
|||||||
return !m_RedundancyGroupName.IsEmpty();
|
return !m_RedundancyGroupName.IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsEmpty() const;
|
bool HasChildren() const;
|
||||||
void AddDependency(const Dependency::Ptr& dependency);
|
void AddDependency(const Dependency::Ptr& dependency);
|
||||||
void RemoveDependency(const Dependency::Ptr& dependency);
|
void RemoveDependency(const Dependency::Ptr& dependency);
|
||||||
std::vector<Dependency::Ptr> GetDependenciesForChild(const Checkable* child) const;
|
std::vector<Dependency::Ptr> GetDependenciesForChild(const Checkable* child) const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user