diff --git a/lib/icinga/checkable-dependency.cpp b/lib/icinga/checkable-dependency.cpp index 47147bd36..51e054e60 100644 --- a/lib/icinga/checkable-dependency.cpp +++ b/lib/icinga/checkable-dependency.cpp @@ -75,7 +75,7 @@ static std::variant GetDependencyGroupKey(const Dependency:: */ void Checkable::AddDependency(const Dependency::Ptr& dependency) { - std::lock_guard lock(m_DependencyMutex); + std::unique_lock lock(m_DependencyMutex); auto dependencyGroupKey(GetDependencyGroupKey(dependency)); if (!m_DependencyGroupsPushedToRegistry) { @@ -88,27 +88,38 @@ void Checkable::AddDependency(const Dependency::Ptr& dependency) } std::set dependencies; + bool removeGroup(false); + + DependencyGroup::Ptr existingGroup; if (auto it(m_DependencyGroups.find(dependencyGroupKey)); it != m_DependencyGroups.end()) { - dependencies = DependencyGroup::Unregister(it->second, this); + existingGroup = it->second; + std::tie(dependencies, removeGroup) = DependencyGroup::Unregister(existingGroup, this); m_DependencyGroups.erase(it); } dependencies.emplace(dependency); - m_DependencyGroups.emplace( - dependencyGroupKey, - DependencyGroup::Register(new DependencyGroup(dependency->GetRedundancyGroup(), dependencies)) - ); + auto dependencyGroup(DependencyGroup::Register(new DependencyGroup(dependency->GetRedundancyGroup(), dependencies))); + m_DependencyGroups.emplace(dependencyGroupKey, dependencyGroup); + + lock.unlock(); + + if (existingGroup) { + dependencies.erase(dependency); + DependencyGroup::OnChildRemoved(existingGroup, {dependencies.begin(), dependencies.end()}, removeGroup); + } + DependencyGroup::OnChildRegistered(this, dependencyGroup); } /** * Remove the provided dependency from the current Checkable list of dependencies. * * @param dependency The dependency to remove. + * @param runtimeRemoved Whether the given dependency object is being removed at runtime. */ -void Checkable::RemoveDependency(const Dependency::Ptr& dependency) +void Checkable::RemoveDependency(const Dependency::Ptr& dependency, bool runtimeRemoved) { - std::lock_guard lock(m_DependencyMutex); + std::unique_lock lock(m_DependencyMutex); auto dependencyGroupKey(GetDependencyGroupKey(dependency)); auto it = m_DependencyGroups.find(dependencyGroupKey); @@ -116,15 +127,27 @@ void Checkable::RemoveDependency(const Dependency::Ptr& dependency) return; } - std::set dependencies(DependencyGroup::Unregister(it->second, this)); + DependencyGroup::Ptr existingGroup(it->second); + auto [dependencies, removeGroup] = DependencyGroup::Unregister(existingGroup, this); + m_DependencyGroups.erase(it); dependencies.erase(dependency); + DependencyGroup::Ptr newDependencyGroup; if (!dependencies.empty()) { - m_DependencyGroups.emplace( - dependencyGroupKey, - DependencyGroup::Register(new DependencyGroup(dependency->GetRedundancyGroup(), dependencies)) - ); + newDependencyGroup = DependencyGroup::Register(new DependencyGroup(dependency->GetRedundancyGroup(), dependencies)); + m_DependencyGroups.emplace(dependencyGroupKey, newDependencyGroup); + } + + lock.unlock(); + + if (runtimeRemoved) { + dependencies.emplace(dependency); + DependencyGroup::OnChildRemoved(existingGroup, {dependencies.begin(), dependencies.end()}, removeGroup); + + if (newDependencyGroup) { + DependencyGroup::OnChildRegistered(this, newDependencyGroup); + } } } diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index c3ac29612..53db79d72 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -189,7 +189,7 @@ public: void PushDependencyGroupsToRegistry(); std::vector> GetDependencyGroups() const; void AddDependency(const intrusive_ptr& dependency); - void RemoveDependency(const intrusive_ptr& dependency); + void RemoveDependency(const intrusive_ptr& dependency, bool runtimeRemoved = false); std::vector > GetDependencies() const; bool HasAnyDependencies() const; diff --git a/lib/icinga/dependency-group.cpp b/lib/icinga/dependency-group.cpp index eabe69a0b..47e43316f 100644 --- a/lib/icinga/dependency-group.cpp +++ b/lib/icinga/dependency-group.cpp @@ -5,6 +5,9 @@ using namespace icinga; +boost::signals2::signal DependencyGroup::OnChildRegistered; +boost::signals2::signal&, bool)> DependencyGroup::OnChildRemoved; + std::mutex DependencyGroup::m_RegistryMutex; DependencyGroup::RegistryType DependencyGroup::m_Registry; @@ -36,15 +39,15 @@ DependencyGroup::Ptr DependencyGroup::Register(const DependencyGroup::Ptr& depen * @param dependencyGroup The dependency group to unregister the child Checkable from. * @param child The child Checkable to detach from the dependency group. * - * @return - Returns the dependency objects of the child Checkable that were member of the provided dependency group. + * @return - Returns the dependency objects of the child Checkable that were member of the provided dependency group + * and a boolean indicating whether the dependency group has been erased from the global registry. */ -std::set DependencyGroup::Unregister(const DependencyGroup::Ptr& dependencyGroup, const Checkable::Ptr& child) +std::pair, bool> DependencyGroup::Unregister(const DependencyGroup::Ptr& dependencyGroup, const Checkable::Ptr& child) { std::lock_guard lock(m_RegistryMutex); - std::vector dependencies; if (auto it(m_Registry.find(dependencyGroup)); it != m_Registry.end()) { - const auto& existingGroup(*it); - dependencies = existingGroup->GetDependenciesForChild(child.get()); + auto existingGroup(*it); + auto dependencies(existingGroup->GetDependenciesForChild(child.get())); for (const auto& dependency : dependencies) { existingGroup->RemoveDependency(dependency); @@ -53,8 +56,9 @@ std::set DependencyGroup::Unregister(const DependencyGroup::Ptr if (existingGroup->IsEmpty()) { m_Registry.erase(it); } + return {{dependencies.begin(), dependencies.end()}, existingGroup->IsEmpty()}; } - return {dependencies.begin(), dependencies.end()}; + return {{}, false}; } /** diff --git a/lib/icinga/dependency.cpp b/lib/icinga/dependency.cpp index a9a7bf372..c45015e8f 100644 --- a/lib/icinga/dependency.cpp +++ b/lib/icinga/dependency.cpp @@ -259,7 +259,7 @@ void Dependency::Stop(bool runtimeRemoved) { ObjectImpl::Stop(runtimeRemoved); - GetChild()->RemoveDependency(this); + GetChild()->RemoveDependency(this, runtimeRemoved); GetParent()->RemoveReverseDependency(this); } diff --git a/lib/icinga/dependency.hpp b/lib/icinga/dependency.hpp index 0f3c257ee..a46b785c4 100644 --- a/lib/icinga/dependency.hpp +++ b/lib/icinga/dependency.hpp @@ -136,7 +136,7 @@ public: DependencyGroup(String name, const std::set& dependencies); static DependencyGroup::Ptr Register(const DependencyGroup::Ptr& dependencyGroup); - static std::set Unregister(const DependencyGroup::Ptr& dependencyGroup, const Checkable::Ptr& child); + static std::pair, bool> Unregister(const DependencyGroup::Ptr& dependencyGroup, const Checkable::Ptr& child); static size_t GetRegistrySize(); static CompositeKeyType MakeCompositeKeyFor(const Dependency::Ptr& dependency); @@ -171,6 +171,9 @@ public: State GetState(DependencyType dt = DependencyState, int rstack = 0) const; + static boost::signals2::signal OnChildRegistered; + static boost::signals2::signal&, bool)> OnChildRemoved; + private: void CopyDependenciesTo(const DependencyGroup::Ptr& dest);