Merge pull request #15060 from AndrewMeadows/avoid-mem-leaked-entities

Case 21494: Avoid mem leaked entities
This commit is contained in:
Shannon Romano 2019-03-01 09:34:20 -08:00 committed by GitHub
commit d68f82eb7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 15 deletions

View file

@ -3773,9 +3773,12 @@ std::map<QString, QString> Application::prepareServerlessDomainContents(QUrl dom
tmpTree->reaverageOctreeElements(); tmpTree->reaverageOctreeElements();
tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0); tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0);
} }
std::map<QString, QString> namedPaths = tmpTree->getNamedPaths();
return tmpTree->getNamedPaths(); // we must manually eraseAllOctreeElements(false) else the tmpTree will mem-leak
tmpTree->eraseAllOctreeElements(false);
return namedPaths;
} }
void Application::loadServerlessDomain(QUrl domainURL) { void Application::loadServerlessDomain(QUrl domainURL) {

View file

@ -232,8 +232,6 @@ void EntityTreeRenderer::clearNonLocalEntities() {
} }
} }
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
} else {
qCWarning(entitiesrenderer) << "EntitityTreeRenderer::clear(), Unexpected null scene, possibly during application shutdown";
} }
_renderablesToUpdate = savedEntities; _renderablesToUpdate = savedEntities;

View file

@ -52,7 +52,15 @@ EntityTree::EntityTree(bool shouldReaverage) :
} }
EntityTree::~EntityTree() { EntityTree::~EntityTree() {
eraseAllOctreeElements(false); // NOTE: to eraseAllOctreeElements() in this context is useless because
// any OctreeElements in the tree still have shared backpointers to this Tree
// which means the dtor never would have been called in the first place!
//
// I'm keeping this useless commented-out line to remind us:
// we don't need shared pointer overhead for EntityTrees.
// TODO: EntityTreeElement::_tree should be raw back pointer.
// AND: EntityItem::_element should be a raw back pointer.
//eraseAllOctreeElements(false); // KEEP THIS
} }
void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) { void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) {
@ -74,16 +82,15 @@ void EntityTree::eraseNonLocalEntities() {
emit clearingEntities(); emit clearingEntities();
if (_simulation) { if (_simulation) {
// This will clear all entities host types including local entities, because local entities // local entities are not in the simulation, so we clear ALL
// are not in the physics simulation
_simulation->clearEntities(); _simulation->clearEntities();
} }
_staleProxies.clear();
QHash<EntityItemID, EntityItemPointer> localMap;
localMap.swap(_entityMap);
QHash<EntityItemID, EntityItemPointer> savedEntities;
this->withWriteLock([&] { this->withWriteLock([&] {
foreach(EntityItemPointer entity, localMap) { QHash<EntityItemID, EntityItemPointer> savedEntities;
// NOTE: lock the Tree first, then lock the _entityMap.
// It should never be done the other way around.
QReadLocker locker(&_entityMapLock);
foreach(EntityItemPointer entity, _entityMap) {
EntityTreeElementPointer element = entity->getElement(); EntityTreeElementPointer element = entity->getElement();
if (element) { if (element) {
element->cleanupNonLocalEntities(); element->cleanupNonLocalEntities();
@ -91,11 +98,16 @@ void EntityTree::eraseNonLocalEntities() {
if (entity->isLocalEntity()) { if (entity->isLocalEntity()) {
savedEntities[entity->getEntityItemID()] = entity; savedEntities[entity->getEntityItemID()] = entity;
} else {
int32_t spaceIndex = entity->getSpaceIndex();
if (spaceIndex != -1) {
// stale spaceIndices will be freed later
_staleProxies.push_back(spaceIndex);
} }
} }
}
_entityMap.swap(savedEntities);
}); });
localMap.clear();
_entityMap = savedEntities;
resetClientEditStats(); resetClientEditStats();
clearDeletedEntities(); clearDeletedEntities();
@ -113,13 +125,13 @@ void EntityTree::eraseNonLocalEntities() {
_needsParentFixup = localEntitiesNeedsParentFixup; _needsParentFixup = localEntitiesNeedsParentFixup;
} }
} }
void EntityTree::eraseAllOctreeElements(bool createNewRoot) { void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
emit clearingEntities(); emit clearingEntities();
if (_simulation) { if (_simulation) {
_simulation->clearEntities(); _simulation->clearEntities();
} }
_staleProxies.clear();
QHash<EntityItemID, EntityItemPointer> localMap; QHash<EntityItemID, EntityItemPointer> localMap;
localMap.swap(_entityMap); localMap.swap(_entityMap);
this->withWriteLock([&] { this->withWriteLock([&] {
@ -128,6 +140,11 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
if (element) { if (element) {
element->cleanupEntities(); element->cleanupEntities();
} }
int32_t spaceIndex = entity->getSpaceIndex();
if (spaceIndex != -1) {
// assume stale spaceIndices will be freed later
_staleProxies.push_back(spaceIndex);
}
} }
}); });
localMap.clear(); localMap.clear();
@ -764,9 +781,9 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
_simulation->prepareEntityForDelete(theEntity); _simulation->prepareEntityForDelete(theEntity);
} }
// keep a record of valid stale spaceIndices so they can be removed from the Space
int32_t spaceIndex = theEntity->getSpaceIndex(); int32_t spaceIndex = theEntity->getSpaceIndex();
if (spaceIndex != -1) { if (spaceIndex != -1) {
// stale spaceIndices will be freed later
_staleProxies.push_back(spaceIndex); _staleProxies.push_back(spaceIndex);
} }
} }