History

Fixing some entity race condition crashes

Brad Davis committed 11 years ago

Commit aa989c31 parent c4c6e7236 files · +1364 −1192Browse files

Showing the changes to libraries/entities/src/EntityScriptingInterface.cpp (1 of 36 files). Show all

Unified · Split

libraries/entities/src/EntityScriptingInterface.cpp+142−138
@@ -72,23 +72,23 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties · 71 unmodified lines
72 // If we have a local entity tree set, then also update it.72 // If we have a local entity tree set, then also update it.
73 bool success = true;73 bool success = true;
74 if (_entityTree) {74 if (_entityTree) {
75 _entityTree->lockForWrite();75 _entityTree->withWriteLock([&] {
76 EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);76 EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
77 if (entity) {77 if (entity) {
78 // This Node is creating a new object. If it's in motion, set this Node as the simulator.78 // This Node is creating a new object. If it's in motion, set this Node as the simulator.
79 auto nodeList = DependencyManager::get<NodeList>();79 auto nodeList = DependencyManager::get<NodeList>();
80 const QUuid myNodeID = nodeList->getSessionUUID();80 const QUuid myNodeID = nodeList->getSessionUUID();
81 propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);81 propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
82 82
83 // and make note of it now, so we can act on it right away.83 // and make note of it now, so we can act on it right away.
84 entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);84 entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
85 85
86 entity->setLastBroadcast(usecTimestampNow());86 entity->setLastBroadcast(usecTimestampNow());
87 } else {87 } else {
88 qCDebug(entities) << "script failed to add new Entity to local Octree";88 qCDebug(entities) << "script failed to add new Entity to local Octree";
89 success = false;89 success = false;
90 }90 }
91 _entityTree->unlock();91 });
92 }92 }
93 93
94 // queue the packet94 // queue the packet
@@ -102,28 +102,26 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties · 7 unmodified lines
102EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {102EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
103 EntityItemProperties results;103 EntityItemProperties results;
104 if (_entityTree) {104 if (_entityTree) {
105 _entityTree->lockForRead();105 _entityTree->withReadLock([&] {
106 106 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
107 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));107 if (entity) {
108 108 results = entity->getProperties();
109 if (entity) {109
110 results = entity->getProperties();110 // TODO: improve sitting points and naturalDimensions in the future,
111 111 // for now we've included the old sitting points model behavior for entity types that are models
112 // TODO: improve sitting points and naturalDimensions in the future,112 // we've also added this hack for setting natural dimensions of models
113 // for now we've included the old sitting points model behavior for entity types that are models113 if (entity->getType() == EntityTypes::Model) {
114 // we've also added this hack for setting natural dimensions of models114 const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
115 if (entity->getType() == EntityTypes::Model) {115 if (geometry) {
116 const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);116 results.setSittingPoints(geometry->sittingPoints);
117 if (geometry) {117 Extents meshExtents = geometry->getUnscaledMeshExtents();
118 results.setSittingPoints(geometry->sittingPoints);118 results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
119 Extents meshExtents = geometry->getUnscaledMeshExtents();119 results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
120 results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);120 }
121 results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
122 }121 }
123 }
124 122
125 }123 }
126 _entityTree->unlock();124 });
127 }125 }
128 126
129 return results;127 return results;
@@ -132,56 +130,59 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit · 2 unmodified lines
132QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties properties) {130QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties properties) {
133 EntityItemID entityID(id);131 EntityItemID entityID(id);
134 // If we have a local entity tree set, then also update it.132 // If we have a local entity tree set, then also update it.
135 if (_entityTree) {133 if (!_entityTree) {
136 _entityTree->lockForWrite();134 queueEntityMessage(PacketType::EntityEdit, entityID, properties);
137 bool updatedEntity = _entityTree->updateEntity(entityID, properties);135 return id;
138 _entityTree->unlock();136 }
139 137
140 if (updatedEntity) {138 bool updatedEntity = false;
141 _entityTree->lockForRead();139 _entityTree->withWriteLock([&] {
142 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);140 updatedEntity = _entityTree->updateEntity(entityID, properties);
143 if (entity) {141 });
144 // make sure the properties has a type, so that the encode can know which properties to include142
145 properties.setType(entity->getType());143
146 bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();144 if (!updatedEntity) {
147 bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges;145 return QUuid();
148 if (hasPhysicsChanges) {146 }
149 auto nodeList = DependencyManager::get<NodeList>();147
150 const QUuid myNodeID = nodeList->getSessionUUID();148 _entityTree->withReadLock([&] {
151 149 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
152 if (entity->getSimulatorID() == myNodeID) {150 if (entity) {
153 // we think we already own the simulation, so make sure to send ALL TerseUpdate properties151 // make sure the properties has a type, so that the encode can know which properties to include
154 if (hasTerseUpdateChanges) {152 properties.setType(entity->getType());
155 entity->getAllTerseUpdateProperties(properties);153 bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();
156 }154 bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges;
157 // TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object 155 if (hasPhysicsChanges) {
158 // is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update 156 auto nodeList = DependencyManager::get<NodeList>();
159 // and instead let the physics simulation decide when to send a terse update. This would remove157 const QUuid myNodeID = nodeList->getSessionUUID();
160 // the "slide-no-rotate" glitch (and typical a double-update) that we see during the "poke rolling158
161 // balls" test. However, even if we solve this problem we still need to provide a "slerp the visible159 if (entity->getSimulatorID() == myNodeID) {
162 // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's160 // we think we already own the simulation, so make sure to send ALL TerseUpdate properties
163 // simulation.161 if (hasTerseUpdateChanges) {
164 162 entity->getAllTerseUpdateProperties(properties);
165 if (entity->getSimulationPriority() < SCRIPT_EDIT_SIMULATION_PRIORITY) {163 }
166 // we re-assert our simulation ownership at a higher priority164 // TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object
167 properties.setSimulationOwner(myNodeID, 165 // is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update
168 glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY));166 // and instead let the physics simulation decide when to send a terse update. This would remove
169 }167 // the "slide-no-rotate" glitch (and typical a double-update) that we see during the "poke rolling
170 } else {168 // balls" test. However, even if we solve this problem we still need to provide a "slerp the visible
171 // we make a bid for simulation ownership169 // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's
172 properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);170 // simulation.
173 entity->flagForOwnership();171
172 if (entity->getSimulationPriority() < SCRIPT_EDIT_SIMULATION_PRIORITY) {
173 // we re-assert our simulation ownership at a higher priority
174 properties.setSimulationOwner(myNodeID,
175 glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY));
174 }176 }
177 } else {
178 // we make a bid for simulation ownership
179 properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
180 entity->flagForOwnership();
175 }181 }
176 entity->setLastBroadcast(usecTimestampNow());
177 }182 }
178 _entityTree->unlock();183 entity->setLastBroadcast(usecTimestampNow());
179 queueEntityMessage(PacketType::EntityEdit, entityID, properties);
180 return id;
181 }184 }
182 return QUuid();185 });
183 }
184
185 queueEntityMessage(PacketType::EntityEdit, entityID, properties);186 queueEntityMessage(PacketType::EntityEdit, entityID, properties);
186 return id;187 return id;
187}188}
@@ -192,18 +193,16 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { · 4 unmodified lines
192 193
193 // If we have a local entity tree set, then also update it.194 // If we have a local entity tree set, then also update it.
194 if (_entityTree) {195 if (_entityTree) {
195 _entityTree->lockForWrite();196 _entityTree->withWriteLock([&] {
196 197 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
197 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);198 if (entity) {
198 if (entity) {199 if (entity->getLocked()) {
199 if (entity->getLocked()) {200 shouldDelete = false;
200 shouldDelete = false;201 } else {
201 } else {202 _entityTree->deleteEntity(entityID);
202 _entityTree->deleteEntity(entityID);203 }
203 }204 }
204 }205 });
205
206 _entityTree->unlock();
207 }206 }
208 207
209 // if at this point, we know the id, and we should still delete the entity, send the update to the entity server208 // if at this point, we know the id, and we should still delete the entity, send the update to the entity server
@@ -215,9 +214,10 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { · 5 unmodified lines
215QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {214QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {
216 EntityItemID result;215 EntityItemID result;
217 if (_entityTree) {216 if (_entityTree) {
218 _entityTree->lockForRead();217 EntityItemPointer closestEntity;
219 EntityItemPointer closestEntity = _entityTree->findClosestEntity(center, radius);218 _entityTree->withReadLock([&] {
220 _entityTree->unlock();219 closestEntity = _entityTree->findClosestEntity(center, radius);
220 });
221 if (closestEntity) {221 if (closestEntity) {
222 result = closestEntity->getEntityItemID();222 result = closestEntity->getEntityItemID();
223 }223 }
@@ -228,19 +228,19 @@ QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float · 4 unmodified lines
228 228
229void EntityScriptingInterface::dumpTree() const {229void EntityScriptingInterface::dumpTree() const {
230 if (_entityTree) {230 if (_entityTree) {
231 _entityTree->lockForRead();231 _entityTree->withReadLock([&] {
232 _entityTree->dumpTree();232 _entityTree->dumpTree();
233 _entityTree->unlock();233 });
234 }234 }
235}235}
236 236
237QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const {237QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const {
238 QVector<QUuid> result;238 QVector<QUuid> result;
239 if (_entityTree) {239 if (_entityTree) {
240 _entityTree->lockForRead();
241 QVector<EntityItemPointer> entities;240 QVector<EntityItemPointer> entities;
242 _entityTree->findEntities(center, radius, entities);241 _entityTree->withReadLock([&] {
243 _entityTree->unlock();242 _entityTree->findEntities(center, radius, entities);
243 });
244 244
245 foreach (EntityItemPointer entity, entities) {245 foreach (EntityItemPointer entity, entities) {
246 result << entity->getEntityItemID();246 result << entity->getEntityItemID();
@@ -252,11 +252,11 @@ QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, f · 5 unmodified lines
252QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {252QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {
253 QVector<QUuid> result;253 QVector<QUuid> result;
254 if (_entityTree) {254 if (_entityTree) {
255 _entityTree->lockForRead();
256 AABox box(corner, dimensions);
257 QVector<EntityItemPointer> entities;255 QVector<EntityItemPointer> entities;
258 _entityTree->findEntities(box, entities);256 _entityTree->withReadLock([&] {
259 _entityTree->unlock();257 AABox box(corner, dimensions);
258 _entityTree->findEntities(box, entities);
259 });
260 260
261 foreach (EntityItemPointer entity, entities) {261 foreach (EntityItemPointer entity, entities) {
262 result << entity->getEntityItemID();262 result << entity->getEntityItemID();
@@ -432,9 +432,10 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID, · 169 unmodified lines
432 }432 }
433 433
434 auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);434 auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
435 _entityTree->lockForWrite();435 bool result;
436 bool result = actor(*polyVoxEntity);436 _entityTree->withWriteLock([&] {
437 _entityTree->unlock();437 result = actor(*polyVoxEntity);
438 });
438 return result;439 return result;
439}440}
440 441
@@ -457,15 +458,17 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(Line · 16 unmodified lines
457 auto now = usecTimestampNow();458 auto now = usecTimestampNow();
458 459
459 auto lineEntity = std::static_pointer_cast<LineEntityItem>(entity);460 auto lineEntity = std::static_pointer_cast<LineEntityItem>(entity);
460 _entityTree->lockForWrite();461 bool success;
461 bool success = actor(*lineEntity);462 _entityTree->withWriteLock([&] {
462 entity->setLastEdited(now);463 success = actor(*lineEntity);
463 entity->setLastBroadcast(now);464 entity->setLastEdited(now);
464 _entityTree->unlock();465 entity->setLastBroadcast(now);
465 466 });
466 _entityTree->lockForRead();467
467 EntityItemProperties properties = entity->getProperties();468 EntityItemProperties properties;
468 _entityTree->unlock();469 _entityTree->withReadLock([&] {
470 properties = entity->getProperties();
471 });
469 472
470 properties.setLinePointsDirty();473 properties.setLinePointsDirty();
471 properties.setLastEdited(now);474 properties.setLastEdited(now);
@@ -543,32 +546,33 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, · 71 unmodified lines
543 return false;546 return false;
544 }547 }
545 548
546 _entityTree->lockForWrite();549 EntityItemPointer entity;
547 550 bool success;
548 EntitySimulation* simulation = _entityTree->getSimulation();551 _entityTree->withWriteLock([&] {
549 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);552 EntitySimulation* simulation = _entityTree->getSimulation();
550 if (!entity) {553 EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
551 qDebug() << "actionWorker -- unknown entity" << entityID;554 if (!entity) {
552 _entityTree->unlock();555 qDebug() << "actionWorker -- unknown entity" << entityID;
553 return false;556 return;
554 }557 }
555 558
556 if (!simulation) {559 if (!simulation) {
557 qDebug() << "actionWorker -- no simulation" << entityID;560 qDebug() << "actionWorker -- no simulation" << entityID;
558 _entityTree->unlock();561 return;
559 return false;562 }
560 }
561 563
562 bool success = actor(simulation, entity);564 success = actor(simulation, entity);
563 if (success) {565 if (success) {
564 _entityTree->entityChanged(entity);566 _entityTree->entityChanged(entity);
565 }567 }
566 _entityTree->unlock();568 });
567 569
568 // transmit the change570 // transmit the change
569 _entityTree->lockForRead();571 EntityItemProperties properties;
570 EntityItemProperties properties = entity->getProperties();572 _entityTree->withReadLock([&] {
571 _entityTree->unlock();573 properties = entity->getProperties();
574 });
575
572 properties.setActionDataDirty();576 properties.setActionDataDirty();
573 auto now = usecTimestampNow();577 auto now = usecTimestampNow();
574 properties.setLastEdited(now);578 properties.setLastEdited(now);