fix crash when deleting entities quickly

This commit is contained in:
Andrew Meadows 2016-01-21 17:30:04 -08:00
parent f22ffa8cdd
commit d8fb61e820
5 changed files with 47 additions and 15 deletions

View file

@ -3089,13 +3089,14 @@ void Application::update(float deltaTime) {
static VectorOfMotionStates motionStates; static VectorOfMotionStates motionStates;
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates); _entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates); _physicsEngine->removeObjects(motionStates);
_entitySimulation.deleteObjectsRemovedFromPhysics();
getEntities()->getTree()->withWriteLock([&] { getEntities()->getTree()->withReadLock([&] {
_entitySimulation.getObjectsToAddToPhysics(motionStates); _entitySimulation.getObjectsToAddToPhysics(motionStates);
_physicsEngine->addObjects(motionStates); _physicsEngine->addObjects(motionStates);
}); });
getEntities()->getTree()->withWriteLock([&] { getEntities()->getTree()->withReadLock([&] {
_entitySimulation.getObjectsToChange(motionStates); _entitySimulation.getObjectsToChange(motionStates);
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
_entitySimulation.setObjectsToChange(stillNeedChange); _entitySimulation.setObjectsToChange(stillNeedChange);

View file

@ -48,6 +48,7 @@ void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete)
} }
void EntitySimulation::removeEntityInternal(EntityItemPointer entity) { void EntitySimulation::removeEntityInternal(EntityItemPointer entity) {
QMutexLocker lock(&_mutex);
// remove from all internal lists except _entitiesToDelete // remove from all internal lists except _entitiesToDelete
_mortalEntities.remove(entity); _mortalEntities.remove(entity);
_entitiesToUpdate.remove(entity); _entitiesToUpdate.remove(entity);
@ -61,6 +62,7 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
assert(entity); assert(entity);
assert(entity->isDead()); assert(entity->isDead());
if (entity->isSimulated()) { if (entity->isSimulated()) {
QMutexLocker lock(&_mutex);
entity->clearActions(this); entity->clearActions(this);
removeEntityInternal(entity); removeEntityInternal(entity);
_entitiesToDelete.insert(entity); _entitiesToDelete.insert(entity);
@ -69,11 +71,13 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
void EntitySimulation::addEntityInternal(EntityItemPointer entity) { void EntitySimulation::addEntityInternal(EntityItemPointer entity) {
if (entity->isMoving() && !entity->getPhysicsInfo()) { if (entity->isMoving() && !entity->getPhysicsInfo()) {
QMutexLocker lock(&_mutex);
_simpleKinematicEntities.insert(entity); _simpleKinematicEntities.insert(entity);
} }
} }
void EntitySimulation::changeEntityInternal(EntityItemPointer entity) { void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
QMutexLocker lock(&_mutex);
if (entity->isMoving() && !entity->getPhysicsInfo()) { if (entity->isMoving() && !entity->getPhysicsInfo()) {
_simpleKinematicEntities.insert(entity); _simpleKinematicEntities.insert(entity);
} else { } else {
@ -86,6 +90,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
if (now > _nextExpiry) { if (now > _nextExpiry) {
// only search for expired entities if we expect to find one // only search for expired entities if we expect to find one
_nextExpiry = quint64(-1); _nextExpiry = quint64(-1);
QMutexLocker lock(&_mutex);
SetOfEntities::iterator itemItr = _mortalEntities.begin(); SetOfEntities::iterator itemItr = _mortalEntities.begin();
while (itemItr != _mortalEntities.end()) { while (itemItr != _mortalEntities.end()) {
EntityItemPointer entity = *itemItr; EntityItemPointer entity = *itemItr;
@ -108,6 +113,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
// protected // protected
void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) { void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
PerformanceTimer perfTimer("updatingEntities"); PerformanceTimer perfTimer("updatingEntities");
QMutexLocker lock(&_mutex);
SetOfEntities::iterator itemItr = _entitiesToUpdate.begin(); SetOfEntities::iterator itemItr = _entitiesToUpdate.begin();
while (itemItr != _entitiesToUpdate.end()) { while (itemItr != _entitiesToUpdate.end()) {
EntityItemPointer entity = *itemItr; EntityItemPointer entity = *itemItr;
@ -124,6 +130,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
// protected // protected
void EntitySimulation::sortEntitiesThatMoved() { void EntitySimulation::sortEntitiesThatMoved() {
QMutexLocker lock(&_mutex);
// NOTE: this is only for entities that have been moved by THIS EntitySimulation. // NOTE: this is only for entities that have been moved by THIS EntitySimulation.
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation. // External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
PerformanceTimer perfTimer("sortingEntities"); PerformanceTimer perfTimer("sortingEntities");

View file

@ -23,6 +23,7 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
// has finished simulating it. // has finished simulating it.
auto nodeList = DependencyManager::get<LimitedNodeList>(); auto nodeList = DependencyManager::get<LimitedNodeList>();
QMutexLocker lock(&_mutex);
SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin(); SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin();
while (itemItr != _entitiesWithSimulator.end()) { while (itemItr != _entitiesWithSimulator.end()) {
EntityItemPointer entity = *itemItr; EntityItemPointer entity = *itemItr;
@ -48,18 +49,21 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
EntitySimulation::addEntityInternal(entity); EntitySimulation::addEntityInternal(entity);
if (!entity->getSimulatorID().isNull()) { if (!entity->getSimulatorID().isNull()) {
QMutexLocker lock(&_mutex);
_entitiesWithSimulator.insert(entity); _entitiesWithSimulator.insert(entity);
} }
} }
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) { void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
EntitySimulation::removeEntityInternal(entity); EntitySimulation::removeEntityInternal(entity);
QMutexLocker lock(&_mutex);
_entitiesWithSimulator.remove(entity); _entitiesWithSimulator.remove(entity);
} }
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) { void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
EntitySimulation::changeEntityInternal(entity); EntitySimulation::changeEntityInternal(entity);
if (!entity->getSimulatorID().isNull()) { if (!entity->getSimulatorID().isNull()) {
QMutexLocker lock(&_mutex);
_entitiesWithSimulator.insert(entity); _entitiesWithSimulator.insert(entity);
} }
entity->clearDirtyFlags(); entity->clearDirtyFlags();

View file

@ -43,6 +43,7 @@ void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) {
} }
void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
QMutexLocker lock(&_mutex);
assert(entity); assert(entity);
assert(!entity->isDead()); assert(!entity->isDead());
if (entity->shouldBePhysical()) { if (entity->shouldBePhysical()) {
@ -57,6 +58,7 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) { void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
EntitySimulation::removeEntityInternal(entity); EntitySimulation::removeEntityInternal(entity);
QMutexLocker lock(&_mutex);
_entitiesToAddToPhysics.remove(entity); _entitiesToAddToPhysics.remove(entity);
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo()); EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
@ -78,8 +80,7 @@ void PhysicalEntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesTo
// rather than do it here // rather than do it here
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo()); EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
if (motionState) { if (motionState) {
delete motionState; _entitiesToRemoveFromPhysics.insert(entity);
entity->setPhysicsInfo(nullptr);
} }
} }
_entitiesToDelete.clear(); _entitiesToDelete.clear();
@ -87,6 +88,7 @@ void PhysicalEntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesTo
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) { void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine // queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
QMutexLocker lock(&_mutex);
assert(entity); assert(entity);
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo()); EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
if (motionState) { if (motionState) {
@ -119,7 +121,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
// while it is in the middle of a simulation step. As it is, we're probably in shutdown mode // while it is in the middle of a simulation step. As it is, we're probably in shutdown mode
// anyway, so maybe the simulation was already properly shutdown? Cross our fingers... // anyway, so maybe the simulation was already properly shutdown? Cross our fingers...
// first disconnect each MotionStates from its Entity // copy everything into _entitiesToDelete
for (auto stateItr : _physicalObjects) { for (auto stateItr : _physicalObjects) {
EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr)); EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));
_entitiesToDelete.insert(motionState->getEntity()); _entitiesToDelete.insert(motionState->getEntity());
@ -134,8 +136,8 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
for (auto entity : _entitiesToDelete) { for (auto entity : _entitiesToDelete) {
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo()); EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
if (motionState) { if (motionState) {
delete motionState;
entity->setPhysicsInfo(nullptr); entity->setPhysicsInfo(nullptr);
delete motionState;
} }
} }
@ -158,22 +160,39 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity)
// end EntitySimulation overrides // end EntitySimulation overrides
void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
_entitiesToRelease.clear();
result.clear(); result.clear();
QMutexLocker lock(&_mutex); QMutexLocker lock(&_mutex);
for (auto entity: _entitiesToRemoveFromPhysics) { for (auto entity: _entitiesToRemoveFromPhysics) {
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo()); // make sure it isn't on any side lists
if (motionState) {
_pendingChanges.remove(motionState);
_physicalObjects.remove(motionState);
result.push_back(motionState);
}
_entitiesToAddToPhysics.remove(entity); _entitiesToAddToPhysics.remove(entity);
if (entity->isDead()) {
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
assert(motionState);
_pendingChanges.remove(motionState);
_physicalObjects.remove(motionState);
result.push_back(motionState);
_entitiesToRelease.insert(entity);
if (entity->isSimulated() && entity->isDead()) {
_entitiesToDelete.insert(entity); _entitiesToDelete.insert(entity);
} }
} }
_entitiesToRemoveFromPhysics.swap(_entitiesToRelease); _entitiesToRemoveFromPhysics.clear();
}
void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() {
QMutexLocker lock(&_mutex);
for (auto entity: _entitiesToRelease) {
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
assert(motionState);
entity->setPhysicsInfo(nullptr);
delete motionState;
if (entity->isSimulated() && entity->isDead()) {
_entitiesToDelete.insert(entity);
}
}
_entitiesToRelease.clear();
} }
void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) { void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) {

View file

@ -49,6 +49,7 @@ public:
virtual void prepareEntityForDelete(EntityItemPointer entity) override; virtual void prepareEntityForDelete(EntityItemPointer entity) override;
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& result); void getObjectsToRemoveFromPhysics(VectorOfMotionStates& result);
void deleteObjectsRemovedFromPhysics();
void getObjectsToAddToPhysics(VectorOfMotionStates& result); void getObjectsToAddToPhysics(VectorOfMotionStates& result);
void setObjectsToChange(const VectorOfMotionStates& objectsToChange); void setObjectsToChange(const VectorOfMotionStates& objectsToChange);
void getObjectsToChange(VectorOfMotionStates& result); void getObjectsToChange(VectorOfMotionStates& result);