Skip to content

Commit 876724b

Browse files
authored
Merge branch 'main' into adlarkin/clone_entities
2 parents d9de439 + f48085b commit 876724b

File tree

4 files changed

+81
-18
lines changed

4 files changed

+81
-18
lines changed

include/ignition/gazebo/EntityComponentManager.hh

+21-3
Original file line numberDiff line numberDiff line change
@@ -712,9 +712,11 @@ namespace ignition
712712
/// \brief Find a view based on the provided component type ids.
713713
/// \param[in] _types The component type ids that serve as a key into
714714
/// a map of views.
715-
/// \return A pointer to the view. nullptr is returned if the view wasn't
716-
/// found.
717-
private: detail::BaseView *FindView(
715+
/// \return A pair containing a the view itself and a mutex that can be
716+
/// used for locking the view while entities are being added to it.
717+
/// If a view defined by _types does not exist, the pair will contain
718+
/// nullptrs.
719+
private: std::pair<detail::BaseView *, std::mutex *> FindView(
718720
const std::vector<ComponentTypeId> &_types) const;
719721

720722
/// \brief Add a new view to the set of stored views.
@@ -752,6 +754,22 @@ namespace ignition
752754
const std::unordered_set<ComponentTypeId> &_types = {},
753755
bool _full = false) const;
754756

757+
/// \brief Set whether views should be locked when entities are being
758+
/// added to them. This can be used to prevent race conditions in
759+
/// system PostUpdates, since these are run in parallel (entities are
760+
/// added to views when the view is used, so if two systems try to access
761+
/// the same view in PostUpdate, we run the risk of multiple threads
762+
/// reading/writing from the same data).
763+
/// \param[in] _lock Whether the views should lock while entities are
764+
/// being added to them (true) or not (false).
765+
private: void LockAddingEntitiesToViews(bool _lock);
766+
767+
/// \brief Get whether views should be locked when entities are being
768+
/// added to them.
769+
/// \return True if views should be locked during entitiy addition, false
770+
/// otherwise.
771+
private: bool LockAddingEntitiesToViews() const;
772+
755773
// Make runners friends so that they can manage entity creation and
756774
// removal. This should be safe since runners are internal
757775
// to Gazebo.

include/ignition/gazebo/detail/EntityComponentManager.hh

+20-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <cstring>
2121
#include <map>
2222
#include <memory>
23+
#include <mutex>
2324
#include <optional>
2425
#include <set>
2526
#include <tuple>
@@ -488,11 +489,29 @@ detail::View<ComponentTypeTs...> *EntityComponentManager::FindView() const
488489
{
489490
auto viewKey = std::vector<ComponentTypeId>{ComponentTypeTs::typeId...};
490491

491-
auto baseViewPtr = this->FindView(viewKey);
492+
auto baseViewMutexPair = this->FindView(viewKey);
493+
auto baseViewPtr = baseViewMutexPair.first;
492494
if (nullptr != baseViewPtr)
493495
{
494496
auto view = static_cast<detail::View<ComponentTypeTs...>*>(baseViewPtr);
495497

498+
std::unique_ptr<std::lock_guard<std::mutex>> viewLock;
499+
if (this->LockAddingEntitiesToViews())
500+
{
501+
// lock the mutex unique to this view in order to prevent multiple threads
502+
// from concurrently reading/modifying the view's toAddEntities data
503+
// (for example, this is useful in system PostUpdates since they are run
504+
// in parallel)
505+
auto mutexPtr = baseViewMutexPair.second;
506+
if (nullptr == mutexPtr)
507+
{
508+
ignerr << "Internal error: requested to lock a view, but no mutex "
509+
<< "exists for this view. This should never happen!" << std::endl;
510+
return view;
511+
}
512+
viewLock = std::make_unique<std::lock_guard<std::mutex>>(*mutexPtr);
513+
}
514+
496515
// add any new entities to the view before using it
497516
for (const auto &[entity, isNew] : view->ToAddEntities())
498517
{

src/EntityComponentManager.cc

+38-14
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,15 @@ class ignition::gazebo::EntityComponentManagerPrivate
138138
public: mutable std::mutex removedComponentsMutex;
139139

140140
/// \brief The set of all views.
141+
/// The value is a pair of the view itself and a mutex that can be used for
142+
/// locking the view to ensure thread safety when adding entities to the view.
141143
public: mutable std::unordered_map<detail::ComponentTypeKey,
142-
std::unique_ptr<detail::BaseView>, detail::ComponentTypeHasher> views;
144+
std::pair<std::unique_ptr<detail::BaseView>,
145+
std::unique_ptr<std::mutex>>, detail::ComponentTypeHasher> views;
146+
147+
/// \brief A flag that indicates whether views should be locked while adding
148+
/// new entities to them or not.
149+
public: bool lockAddEntitiesToViews{false};
143150

144151
/// \brief Cache of previously queried descendants. The key is the parent
145152
/// entity for which descendants were queried, and the value are all its
@@ -417,7 +424,7 @@ void EntityComponentManager::ClearNewlyCreatedEntities()
417424

418425
for (auto &view : this->dataPtr->views)
419426
{
420-
view.second->ResetNewEntityState();
427+
view.second.first->ResetNewEntityState();
421428
}
422429
}
423430

@@ -465,7 +472,7 @@ void EntityComponentManager::RequestRemoveEntity(Entity _entity,
465472
{
466473
for (auto &view : this->dataPtr->views)
467474
{
468-
view.second->MarkEntityToRemove(removedEntity);
475+
view.second.first->MarkEntityToRemove(removedEntity);
469476
}
470477
}
471478
}
@@ -523,7 +530,7 @@ void EntityComponentManager::ProcessRemoveEntityRequests()
523530
// Remove the entity from views.
524531
for (auto &view : this->dataPtr->views)
525532
{
526-
view.second->RemoveEntity(entity);
533+
view.second.first->RemoveEntity(entity);
527534
}
528535
}
529536
// Clear the set of entities to remove.
@@ -566,7 +573,7 @@ bool EntityComponentManager::RemoveComponent(
566573

567574
// update views to reflect the component removal
568575
for (auto &viewPair : this->dataPtr->views)
569-
viewPair.second->NotifyComponentRemoval(_entity, _typeId);
576+
viewPair.second.first->NotifyComponentRemoval(_entity, _typeId);
570577
}
571578

572579
this->dataPtr->AddModifiedComponent(_entity);
@@ -802,7 +809,7 @@ bool EntityComponentManager::CreateComponentImplementation(
802809
updateData = false;
803810
for (auto &viewPair : this->dataPtr->views)
804811
{
805-
auto &view = viewPair.second;
812+
auto &view = viewPair.second.first;
806813
if (this->EntityMatches(_entity, view->ComponentTypes()))
807814
view->MarkEntityToAdd(_entity, this->IsNewEntity(_entity));
808815
}
@@ -833,7 +840,7 @@ bool EntityComponentManager::CreateComponentImplementation(
833840

834841
for (auto &viewPair : this->dataPtr->views)
835842
{
836-
viewPair.second->NotifyComponentAddition(_entity,
843+
viewPair.second.first->NotifyComponentAddition(_entity,
837844
this->IsNewEntity(_entity), _componentTypeId);
838845
}
839846
}
@@ -941,14 +948,18 @@ const EntityGraph &EntityComponentManager::Entities() const
941948
}
942949

943950
//////////////////////////////////////////////////
944-
detail::BaseView *EntityComponentManager::FindView(
951+
std::pair<detail::BaseView *, std::mutex *> EntityComponentManager::FindView(
945952
const std::vector<ComponentTypeId> &_types) const
946953
{
947954
std::lock_guard<std::mutex> lockViews(this->dataPtr->viewsMutex);
955+
std::pair<detail::BaseView *, std::mutex *> viewMutexPair(nullptr, nullptr);
948956
auto iter = this->dataPtr->views.find(_types);
949957
if (iter != this->dataPtr->views.end())
950-
return iter->second.get();
951-
return nullptr;
958+
{
959+
viewMutexPair.first = iter->second.first.get();
960+
viewMutexPair.second = iter->second.second.get();
961+
}
962+
return viewMutexPair;
952963
}
953964

954965
//////////////////////////////////////////////////
@@ -959,9 +970,10 @@ detail::BaseView *EntityComponentManager::AddView(
959970
// If the view already exists, then the map will return the iterator to
960971
// the location that prevented the insertion.
961972
std::lock_guard<std::mutex> lockViews(this->dataPtr->viewsMutex);
962-
auto iter = this->dataPtr->views.insert(
963-
std::make_pair(_types, std::move(_view))).first;
964-
return iter->second.get();
973+
auto iter = this->dataPtr->views.insert(std::make_pair(_types,
974+
std::make_pair(std::move(_view),
975+
std::make_unique<std::mutex>()))).first;
976+
return iter->second.first.get();
965977
}
966978

967979
//////////////////////////////////////////////////
@@ -970,7 +982,7 @@ void EntityComponentManager::RebuildViews()
970982
IGN_PROFILE("EntityComponentManager::RebuildViews");
971983
for (auto &viewPair : this->dataPtr->views)
972984
{
973-
auto &view = viewPair.second;
985+
auto &view = viewPair.second.first;
974986
view->Reset();
975987

976988
// Add all the entities that match the component types to the
@@ -1700,6 +1712,18 @@ void EntityComponentManager::SetEntityCreateOffset(uint64_t _offset)
17001712
this->dataPtr->entityCount = _offset;
17011713
}
17021714

1715+
/////////////////////////////////////////////////
1716+
void EntityComponentManager::LockAddingEntitiesToViews(bool _lock)
1717+
{
1718+
this->dataPtr->lockAddEntitiesToViews = _lock;
1719+
}
1720+
1721+
/////////////////////////////////////////////////
1722+
bool EntityComponentManager::LockAddingEntitiesToViews() const
1723+
{
1724+
return this->dataPtr->lockAddEntitiesToViews;
1725+
}
1726+
17031727
/////////////////////////////////////////////////
17041728
void EntityComponentManagerPrivate::AddModifiedComponent(const Entity &_entity)
17051729
{

src/SimulationRunner.cc

+2
Original file line numberDiff line numberDiff line change
@@ -572,13 +572,15 @@ void SimulationRunner::UpdateSystems()
572572

573573
{
574574
IGN_PROFILE("PostUpdate");
575+
this->entityCompMgr.LockAddingEntitiesToViews(true);
575576
// If no systems implementing PostUpdate have been added, then
576577
// the barriers will be uninitialized, so guard against that condition.
577578
if (this->postUpdateStartBarrier && this->postUpdateStopBarrier)
578579
{
579580
this->postUpdateStartBarrier->Wait();
580581
this->postUpdateStopBarrier->Wait();
581582
}
583+
this->entityCompMgr.LockAddingEntitiesToViews(false);
582584
}
583585
}
584586

0 commit comments

Comments
 (0)