lock the entity tree during physics operations that access then entity tree. if the simulation and entity-tree are both going to be locked, be sure to lock the entity tree first (and unlock it last), because this is what the network-reading thread does

This commit is contained in:
Seth Alves 2015-06-26 07:00:28 -07:00
parent 63812f28ad
commit a0d77c061c
3 changed files with 95 additions and 22 deletions

View file

@ -2496,24 +2496,45 @@ void Application::update(float deltaTime) {
_entitySimulation.lock(); _entitySimulation.lock();
_physicsEngine.deleteObjects(_entitySimulation.getObjectsToDelete()); _physicsEngine.deleteObjects(_entitySimulation.getObjectsToDelete());
_entitySimulation.unlock();
_entities.getTree()->lockForWrite();
_entitySimulation.lock();
_physicsEngine.addObjects(_entitySimulation.getObjectsToAdd()); _physicsEngine.addObjects(_entitySimulation.getObjectsToAdd());
_entitySimulation.unlock();
_entities.getTree()->unlock();
_entities.getTree()->lockForWrite();
_entitySimulation.lock();
_physicsEngine.changeObjects(_entitySimulation.getObjectsToChange()); _physicsEngine.changeObjects(_entitySimulation.getObjectsToChange());
_entitySimulation.unlock();
_entities.getTree()->unlock();
_entitySimulation.lock();
_entitySimulation.applyActionChanges(); _entitySimulation.applyActionChanges();
_entitySimulation.unlock(); _entitySimulation.unlock();
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data(); AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
_physicsEngine.deleteObjects(avatarManager->getObjectsToDelete()); _physicsEngine.deleteObjects(avatarManager->getObjectsToDelete());
_physicsEngine.addObjects(avatarManager->getObjectsToAdd()); _physicsEngine.addObjects(avatarManager->getObjectsToAdd());
_physicsEngine.changeObjects(avatarManager->getObjectsToChange()); _physicsEngine.changeObjects(avatarManager->getObjectsToChange());
_entities.getTree()->lockForWrite();
_physicsEngine.stepSimulation(); _physicsEngine.stepSimulation();
_entities.getTree()->unlock();
if (_physicsEngine.hasOutgoingChanges()) { if (_physicsEngine.hasOutgoingChanges()) {
_entities.getTree()->lockForWrite();
_entitySimulation.lock(); _entitySimulation.lock();
_entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID()); _entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID());
_entitySimulation.unlock(); _entitySimulation.unlock();
_entities.getTree()->unlock();
_entities.getTree()->lockForWrite();
avatarManager->handleOutgoingChanges(_physicsEngine.getOutgoingChanges()); avatarManager->handleOutgoingChanges(_physicsEngine.getOutgoingChanges());
_entities.getTree()->unlock();
auto collisionEvents = _physicsEngine.getCollisionEvents(); auto collisionEvents = _physicsEngine.getCollisionEvents();
avatarManager->handleCollisionEvents(collisionEvents); avatarManager->handleCollisionEvents(collisionEvents);

View file

@ -19,6 +19,10 @@
#include "PhysicsHelpers.h" #include "PhysicsHelpers.h"
#include "PhysicsLogging.h" #include "PhysicsLogging.h"
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
#include "EntityTree.h"
#endif
static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f;
static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4;
@ -42,6 +46,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
{ {
_type = MOTIONSTATE_TYPE_ENTITY; _type = MOTIONSTATE_TYPE_ENTITY;
assert(_entity != nullptr); assert(_entity != nullptr);
assert(entityTreeIsLocked());
setMass(_entity->computeMass()); setMass(_entity->computeMass());
} }
@ -50,7 +55,35 @@ EntityMotionState::~EntityMotionState() {
assert(!_entity); assert(!_entity);
} }
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
bool EntityMotionState::entityTreeIsLocked() const {
EntityTreeElement* element = _entity ? _entity->getElement() : nullptr;
EntityTree* tree = element ? element->getTree() : nullptr;
if (tree) {
bool readSuccess = tree->tryLockForRead();
if (readSuccess) {
tree->unlock();
}
bool writeSuccess = tree->tryLockForWrite();
if (writeSuccess) {
tree->unlock();
}
if (readSuccess && writeSuccess) {
return false; // if we can take either kind of lock, there was no tree lock.
}
return true; // either read or write failed, so there is some lock in place.
} else {
return true;
}
}
#else
bool EntityMotionState::entityTreeIsLocked() const {
return true;
}
#endif
void EntityMotionState::updateServerPhysicsVariables() { void EntityMotionState::updateServerPhysicsVariables() {
assert(entityTreeIsLocked());
_serverPosition = _entity->getPosition(); _serverPosition = _entity->getPosition();
_serverRotation = _entity->getRotation(); _serverRotation = _entity->getRotation();
_serverVelocity = _entity->getVelocity(); _serverVelocity = _entity->getVelocity();
@ -60,6 +93,7 @@ void EntityMotionState::updateServerPhysicsVariables() {
// virtual // virtual
void EntityMotionState::handleEasyChanges(uint32_t flags) { void EntityMotionState::handleEasyChanges(uint32_t flags) {
assert(entityTreeIsLocked());
updateServerPhysicsVariables(); updateServerPhysicsVariables();
ObjectMotionState::handleEasyChanges(flags); ObjectMotionState::handleEasyChanges(flags);
if (flags & EntityItem::DIRTY_SIMULATOR_ID) { if (flags & EntityItem::DIRTY_SIMULATOR_ID) {
@ -101,6 +135,7 @@ MotionType EntityMotionState::computeObjectMotionType() const {
if (!_entity) { if (!_entity) {
return MOTION_TYPE_STATIC; return MOTION_TYPE_STATIC;
} }
assert(entityTreeIsLocked());
if (_entity->getCollisionsWillMove()) { if (_entity->getCollisionsWillMove()) {
return MOTION_TYPE_DYNAMIC; return MOTION_TYPE_DYNAMIC;
} }
@ -108,6 +143,7 @@ MotionType EntityMotionState::computeObjectMotionType() const {
} }
bool EntityMotionState::isMoving() const { bool EntityMotionState::isMoving() const {
assert(entityTreeIsLocked());
return _entity && _entity->isMoving(); return _entity && _entity->isMoving();
} }
@ -120,6 +156,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
if (!_entity) { if (!_entity) {
return; return;
} }
assert(entityTreeIsLocked());
if (_motionType == MOTION_TYPE_KINEMATIC) { if (_motionType == MOTION_TYPE_KINEMATIC) {
// This is physical kinematic motion which steps strictly by the subframe count // This is physical kinematic motion which steps strictly by the subframe count
// of the physics simulation. // of the physics simulation.
@ -140,6 +177,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
if (!_entity) { if (!_entity) {
return; return;
} }
assert(entityTreeIsLocked());
measureBodyAcceleration(); measureBodyAcceleration();
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
_entity->setRotation(bulletToGLM(worldTrans.getRotation())); _entity->setRotation(bulletToGLM(worldTrans.getRotation()));
@ -164,9 +202,12 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
#ifdef WANT_DEBUG #ifdef WANT_DEBUG
quint64 now = usecTimestampNow(); quint64 now = usecTimestampNow();
qCDebug(physics) << "EntityMotionState::setWorldTransform()... changed entity:" << _entity->getEntityItemID(); qCDebug(physics) << "EntityMotionState::setWorldTransform()... changed entity:" << _entity->getEntityItemID();
qCDebug(physics) << " last edited:" << _entity->getLastEdited() << formatUsecTime(now - _entity->getLastEdited()) << "ago"; qCDebug(physics) << " last edited:" << _entity->getLastEdited()
qCDebug(physics) << " last simulated:" << _entity->getLastSimulated() << formatUsecTime(now - _entity->getLastSimulated()) << "ago"; << formatUsecTime(now - _entity->getLastEdited()) << "ago";
qCDebug(physics) << " last updated:" << _entity->getLastUpdated() << formatUsecTime(now - _entity->getLastUpdated()) << "ago"; qCDebug(physics) << " last simulated:" << _entity->getLastSimulated()
<< formatUsecTime(now - _entity->getLastSimulated()) << "ago";
qCDebug(physics) << " last updated:" << _entity->getLastUpdated()
<< formatUsecTime(now - _entity->getLastUpdated()) << "ago";
#endif #endif
} }
@ -174,6 +215,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
btCollisionShape* EntityMotionState::computeNewShape() { btCollisionShape* EntityMotionState::computeNewShape() {
if (_entity) { if (_entity) {
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
assert(entityTreeIsLocked());
_entity->computeShapeInfo(shapeInfo); _entity->computeShapeInfo(shapeInfo);
return getShapeManager()->getShape(shapeInfo); return getShapeManager()->getShape(shapeInfo);
} }
@ -184,6 +226,7 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
if (!_body || !_entity) { if (!_body || !_entity) {
return false; return false;
} }
assert(entityTreeIsLocked());
return _candidateForOwnership || sessionID == _entity->getSimulatorID(); return _candidateForOwnership || sessionID == _entity->getSimulatorID();
} }
@ -297,6 +340,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL. // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
assert(_entity); assert(_entity);
assert(_body); assert(_body);
assert(entityTreeIsLocked());
if (!remoteSimulationOutOfSync(simulationStep)) { if (!remoteSimulationOutOfSync(simulationStep)) {
_candidateForOwnership = false; _candidateForOwnership = false;
@ -326,6 +370,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) { void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) {
assert(_entity); assert(_entity);
assert(entityTreeIsLocked());
bool active = _body->isActive(); bool active = _body->isActive();
if (!active) { if (!active) {
@ -436,6 +481,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
} }
uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() {
assert(entityTreeIsLocked());
uint32_t dirtyFlags = 0; uint32_t dirtyFlags = 0;
if (_body && _entity) { if (_body && _entity) {
dirtyFlags = _entity->getDirtyFlags(); dirtyFlags = _entity->getDirtyFlags();
@ -455,6 +501,7 @@ uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() {
// virtual // virtual
QUuid EntityMotionState::getSimulatorID() const { QUuid EntityMotionState::getSimulatorID() const {
if (_entity) { if (_entity) {
assert(entityTreeIsLocked());
return _entity->getSimulatorID(); return _entity->getSimulatorID();
} }
return QUuid(); return QUuid();
@ -514,6 +561,7 @@ void EntityMotionState::setMotionType(MotionType motionType) {
// virtual // virtual
QString EntityMotionState::getName() { QString EntityMotionState::getName() {
if (_entity) { if (_entity) {
assert(entityTreeIsLocked());
return _entity->getName(); return _entity->getName();
} }
return ""; return "";

View file

@ -83,6 +83,10 @@ public:
friend class PhysicalEntitySimulation; friend class PhysicalEntitySimulation;
protected: protected:
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
bool entityTreeIsLocked() const;
#endif
virtual btCollisionShape* computeNewShape(); virtual btCollisionShape* computeNewShape();
virtual void clearObjectBackPointer(); virtual void clearObjectBackPointer();
virtual void setMotionType(MotionType motionType); virtual void setMotionType(MotionType motionType);