mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-06 09:09:40 +02:00
Protect every access to zone tiles with a mutex
This commit is contained in:
@@ -40,7 +40,30 @@ void ObjectManager::process()
|
||||
void ObjectManager::init()
|
||||
{
|
||||
DEPENDENCY(WaterAdopter);
|
||||
DEPENDENCY_ALL(ConnectionsPlacer); //Monoliths can be placed by other zone, too
|
||||
|
||||
//Monoliths can be placed by other zone, too
|
||||
// Consider only connected zones
|
||||
auto id = zone.getId();
|
||||
std::set<TRmgTemplateZoneId> connectedZones;
|
||||
for(auto c : map.getMapGenOptions().getMapTemplate()->getConnectedZoneIds())
|
||||
{
|
||||
// Only consider connected zones
|
||||
if (c.getZoneA() == id || c.getZoneB() == id)
|
||||
{
|
||||
connectedZones.insert(c.getZoneA());
|
||||
connectedZones.insert(c.getZoneB());
|
||||
}
|
||||
}
|
||||
auto zones = map.getZones();
|
||||
for (auto zoneId : connectedZones)
|
||||
{
|
||||
auto * cp = zones.at(zoneId)->getModificator<ConnectionsPlacer>();
|
||||
if (cp)
|
||||
{
|
||||
dependency(cp);
|
||||
}
|
||||
}
|
||||
|
||||
DEPENDENCY(TownPlacer); //Only secondary towns
|
||||
DEPENDENCY(MinePlacer);
|
||||
POSTFUNCTION(RoadPlacer);
|
||||
@@ -49,9 +72,11 @@ void ObjectManager::init()
|
||||
|
||||
void ObjectManager::createDistancesPriorityQueue()
|
||||
{
|
||||
const auto tiles = zone.areaPossible()->getTilesVector();
|
||||
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
tilesByDistance.clear();
|
||||
for(const auto & tile : zone.areaPossible().getTilesVector())
|
||||
for(const auto & tile : tiles)
|
||||
{
|
||||
tilesByDistance.push(std::make_pair(tile, map.getNearestObjectDistance(tile)));
|
||||
}
|
||||
@@ -93,9 +118,24 @@ void ObjectManager::updateDistances(const int3 & pos)
|
||||
|
||||
void ObjectManager::updateDistances(std::function<ui32(const int3 & tile)> distanceFunction)
|
||||
{
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
// Workaround to avoid dealock when accessed from other zone
|
||||
RecursiveLock lock(zone.areaMutex, boost::try_to_lock);
|
||||
if (!lock.owns_lock())
|
||||
{
|
||||
// Sorry, unsolvable problem of mutual impact¯\_(ツ)_/¯
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Update map distances - Requires lock on zone area only
|
||||
2. Update tilesByDistance priority queue - Requires lock area AND externalAccessMutex
|
||||
*/
|
||||
|
||||
const auto tiles = zone.areaPossible()->getTilesVector();
|
||||
//RecursiveLock lock(externalAccessMutex);
|
||||
tilesByDistance.clear();
|
||||
for (const auto & tile : zone.areaPossible().getTilesVector()) //don't need to mark distance for not possible tiles
|
||||
|
||||
for (const auto & tile : tiles) //don't need to mark distance for not possible tiles
|
||||
{
|
||||
ui32 d = distanceFunction(tile);
|
||||
map.setNearestObjectDistance(tile, std::min(static_cast<float>(d), map.getNearestObjectDistance(tile)));
|
||||
@@ -145,7 +185,10 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
|
||||
|
||||
if(optimizer & OptimizeType::DISTANCE)
|
||||
{
|
||||
// Do not add or remove tiles while we iterate on them
|
||||
//RecursiveLock lock(externalAccessMutex);
|
||||
auto open = tilesByDistance;
|
||||
|
||||
while(!open.empty())
|
||||
{
|
||||
auto node = open.top();
|
||||
@@ -235,7 +278,7 @@ int3 ObjectManager::findPlaceForObject(const rmg::Area & searchArea, rmg::Object
|
||||
|
||||
rmg::Path ObjectManager::placeAndConnectObject(const rmg::Area & searchArea, rmg::Object & obj, si32 min_dist, bool isGuarded, bool onlyStraight, OptimizeType optimizer) const
|
||||
{
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
//RecursiveLock lock(externalAccessMutex);
|
||||
return placeAndConnectObject(searchArea, obj, [this, min_dist, &obj](const int3 & tile)
|
||||
{
|
||||
float bestDistance = 10e9;
|
||||
@@ -372,7 +415,7 @@ bool ObjectManager::createMonoliths()
|
||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, true);
|
||||
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
||||
auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
||||
|
||||
if(!path.valid())
|
||||
{
|
||||
@@ -395,7 +438,7 @@ bool ObjectManager::createRequiredObjects()
|
||||
{
|
||||
logGlobal->trace("Creating required objects");
|
||||
|
||||
//RecursiveLock lock(externalAccessMutex); //Why could requiredObjects be modified during the loop?
|
||||
RecursiveLock lock(externalAccessMutex); //In case someone adds more objects
|
||||
for(const auto & objInfo : requiredObjects)
|
||||
{
|
||||
rmg::Object rmgObject(*objInfo.obj);
|
||||
@@ -403,7 +446,7 @@ bool ObjectManager::createRequiredObjects()
|
||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
||||
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
||||
auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject, 3, guarded, false, OptimizeType::DISTANCE);
|
||||
|
||||
if(!path.valid())
|
||||
{
|
||||
@@ -421,7 +464,7 @@ bool ObjectManager::createRequiredObjects()
|
||||
|
||||
rmg::Object rmgNearObject(*nearby.obj);
|
||||
rmg::Area possibleArea(rmgObject.instances().front()->getBlockedArea().getBorderOutside());
|
||||
possibleArea.intersect(zone.areaPossible());
|
||||
possibleArea.intersect(zone.areaPossible().get());
|
||||
if(possibleArea.empty())
|
||||
{
|
||||
rmgNearObject.clear();
|
||||
@@ -436,12 +479,11 @@ bool ObjectManager::createRequiredObjects()
|
||||
for(const auto & objInfo : closeObjects)
|
||||
{
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
auto possibleArea = zone.areaPossible();
|
||||
|
||||
rmg::Object rmgObject(*objInfo.obj);
|
||||
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
|
||||
bool guarded = addGuard(rmgObject, objInfo.guardStrength, (objInfo.obj->ID == Obj::MONOLITH_TWO_WAY));
|
||||
auto path = placeAndConnectObject(zone.areaPossible(), rmgObject,
|
||||
auto path = placeAndConnectObject(zone.areaPossible().get(), rmgObject,
|
||||
[this, &rmgObject](const int3 & tile)
|
||||
{
|
||||
float dist = rmgObject.getArea().distanceSqr(zone.getPos());
|
||||
@@ -470,15 +512,15 @@ bool ObjectManager::createRequiredObjects()
|
||||
|
||||
rmg::Object rmgNearObject(*nearby.obj);
|
||||
std::set<int3> blockedArea = targetObject->getBlockedPos();
|
||||
rmg::Area possibleArea(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
|
||||
possibleArea.intersect(zone.areaPossible());
|
||||
if(possibleArea.empty())
|
||||
rmg::Area areaForObject(rmg::Area(rmg::Tileset(blockedArea.begin(), blockedArea.end())).getBorderOutside());
|
||||
areaForObject.intersect(zone.areaPossible().get());
|
||||
if(areaForObject.empty())
|
||||
{
|
||||
rmgNearObject.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(possibleArea.getTiles(), zone.getRand()));
|
||||
rmgNearObject.setPosition(*RandomGeneratorUtil::nextItem(areaForObject.getTiles(), zone.getRand()));
|
||||
placeObject(rmgNearObject, false, false);
|
||||
auto path = zone.searchPath(rmgNearObject.getVisitablePosition(), false);
|
||||
if (path.valid())
|
||||
@@ -515,8 +557,6 @@ bool ObjectManager::createRequiredObjects()
|
||||
|
||||
void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateDistance, bool createRoad/* = false*/)
|
||||
{
|
||||
//object.finalize(map);
|
||||
|
||||
if (object.instances().size() == 1 && object.instances().front()->object().ID == Obj::MONSTER)
|
||||
{
|
||||
//Fix for HoTA offset - lonely guards
|
||||
@@ -539,31 +579,33 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
||||
}
|
||||
object.finalize(map, zone.getRand());
|
||||
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
zone.areaPossible().subtract(object.getArea());
|
||||
bool keepVisitable = zone.freePaths().contains(object.getVisitablePosition());
|
||||
zone.freePaths().subtract(object.getArea()); //just to avoid areas overlapping
|
||||
if(keepVisitable)
|
||||
zone.freePaths().add(object.getVisitablePosition());
|
||||
zone.areaUsed().unite(object.getArea());
|
||||
zone.areaUsed().erase(object.getVisitablePosition());
|
||||
|
||||
if(guarded) //We assume the monster won't be guarded
|
||||
{
|
||||
auto guardedArea = object.instances().back()->getAccessibleArea();
|
||||
guardedArea.add(object.instances().back()->getVisitablePosition());
|
||||
auto areaToBlock = object.getAccessibleArea(true);
|
||||
areaToBlock.subtract(guardedArea);
|
||||
zone.areaPossible().subtract(areaToBlock);
|
||||
for(const auto & i : areaToBlock.getTilesVector())
|
||||
if(map.isOnMap(i) && map.isPossible(i))
|
||||
map.setOccupied(i, ETileType::BLOCKED);
|
||||
Zone::Lock lock(zone.areaMutex);
|
||||
|
||||
zone.areaPossible()->subtract(object.getArea());
|
||||
bool keepVisitable = zone.freePaths()->contains(object.getVisitablePosition());
|
||||
zone.freePaths()->subtract(object.getArea()); //just to avoid areas overlapping
|
||||
if(keepVisitable)
|
||||
zone.freePaths()->add(object.getVisitablePosition());
|
||||
zone.areaUsed()->unite(object.getArea());
|
||||
zone.areaUsed()->erase(object.getVisitablePosition());
|
||||
|
||||
if(guarded) //We assume the monster won't be guarded
|
||||
{
|
||||
auto guardedArea = object.instances().back()->getAccessibleArea();
|
||||
guardedArea.add(object.instances().back()->getVisitablePosition());
|
||||
auto areaToBlock = object.getAccessibleArea(true);
|
||||
areaToBlock.subtract(guardedArea);
|
||||
zone.areaPossible()->subtract(areaToBlock);
|
||||
for(const auto & i : areaToBlock.getTilesVector())
|
||||
if(map.isOnMap(i) && map.isPossible(i))
|
||||
map.setOccupied(i, ETileType::BLOCKED);
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
|
||||
if (updateDistance)
|
||||
{
|
||||
//Update distances in every adjacent zone in case of wide connection
|
||||
//Update distances in every adjacent zone (including this one) in case of wide connection
|
||||
|
||||
std::set<TRmgTemplateZoneId> adjacentZones;
|
||||
auto objectArea = object.getArea();
|
||||
@@ -577,8 +619,15 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
|
||||
}
|
||||
}
|
||||
|
||||
for (auto id : adjacentZones)
|
||||
// FIXME: Possible deadlock by two managers updating each other
|
||||
// At this point areaMutex is locked
|
||||
// TODO: Generic function for multiple spinlocks
|
||||
//std::vector sorted(adjacentZones.begin(), adjacentZones.end());
|
||||
//std::sort(sorted.begin(), sorted.end());
|
||||
//for (auto id : adjacentZones)
|
||||
..for (auto id : sorted)
|
||||
{
|
||||
//TODO: Test again with sorted order?
|
||||
auto otherZone = map.getZones().at(id);
|
||||
if ((otherZone->getType() == ETemplateZoneType::WATER) == (zone.getType() == ETemplateZoneType::WATER))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user