diff --git a/indra/newview/ao.cpp b/indra/newview/ao.cpp
index ed9b8b4e3e..f709079fde 100644
--- a/indra/newview/ao.cpp
+++ b/indra/newview/ao.cpp
@@ -424,7 +424,7 @@ void FloaterAO::onClickReload()
mSelectedSet=0;
mSelectedState=0;
- AOEngine::instance().reload();
+ AOEngine::instance().reload(false);
updateList();
}
diff --git a/indra/newview/aoengine.cpp b/indra/newview/aoengine.cpp
index a6358ba65f..6ad5860a50 100644
--- a/indra/newview/aoengine.cpp
+++ b/indra/newview/aoengine.cpp
@@ -66,7 +66,7 @@ AOEngine::AOEngine() :
AOEngine::~AOEngine()
{
- clear();
+ clear(false);
}
void AOEngine::init()
@@ -80,10 +80,23 @@ void AOEngine::onLoginComplete()
AOEngine::instance().init();
}
-void AOEngine::clear()
+void AOEngine::clear( bool aFromTimer )
{
+ mOldSets.insert( mOldSets.end(), mSets.begin(), mSets.end() );
mSets.clear();
+
mCurrentSet=0;
+
+ // FIRE-3801; We cannot delete any AOSet object if we're called from a timer tick. AOSet is derived from LLEventTimer and destruction will
+ // fail in ~LLInstanceTracker when a destructor runs during iteration.
+ if( !aFromTimer )
+ {
+ std::for_each( mOldSets.begin(), mOldSets.end(), DeletePointer( ) );
+ mOldSets.clear();
+
+ std::for_each( mOldImportSets.begin(), mOldImportSets.end(), DeletePointer() );
+ mOldImportSets.clear();
+ }
}
void AOEngine::stopAllStandVariants()
@@ -986,7 +999,7 @@ void AOEngine::update()
}
}
-void AOEngine::reload()
+void AOEngine::reload( bool aFromTimer )
{
BOOL wasEnabled=mEnabled;
@@ -998,7 +1011,7 @@ void AOEngine::reload()
gAgent.stopCurrentAnimations();
mLastOverriddenMotion=ANIM_AGENT_STAND;
- clear();
+ clear( aFromTimer );
mAOFolder.setNull();
mTimerCollection.enableInventoryTimer(TRUE);
tick();
@@ -1531,10 +1544,10 @@ void AOEngine::parseNotecard(const char* buffer)
mTimerCollection.enableImportTimer(TRUE);
mImportRetryCount=0;
- processImport();
+ processImport(false);
}
-void AOEngine::processImport()
+void AOEngine::processImport( bool aFromTimer )
{
if(mImportCategory.isNull())
{
@@ -1597,10 +1610,10 @@ void AOEngine::processImport()
if(allComplete)
{
mTimerCollection.enableImportTimer(FALSE);
- delete mImportSet;
+ mOldImportSets.push_back( mImportSet ); // FIRE-3801; Cannot delete here, or LLInstanceTracker gets upset. Just remember and delete mOldImportSets once we can.
mImportSet=0;
mImportCategory.setNull();
- reload();
+ reload( aFromTimer );
}
}
@@ -1673,12 +1686,12 @@ BOOL AOTimerCollection::tick()
if(mReloadTimer)
{
lldebugs << "Reload timer tick()" << llendl;
- AOEngine::instance().reload();
+ AOEngine::instance().reload(true);
}
if(mImportTimer)
{
lldebugs << "Import timer tick()" << llendl;
- AOEngine::instance().processImport();
+ AOEngine::instance().processImport(true);
}
// always return FALSE or the LLEventTimer will be deleted -> crash
diff --git a/indra/newview/aoengine.h b/indra/newview/aoengine.h
index 9d5d9fb6a3..4535f58866 100644
--- a/indra/newview/aoengine.h
+++ b/indra/newview/aoengine.h
@@ -101,9 +101,9 @@ class AOEngine
const LLUUID override(const LLUUID motion,BOOL start);
void tick();
void update();
- void reload();
+ void reload(bool);
void reloadStateAnimations(AOSet::AOState* state);
- void clear();
+ void clear( bool );
const LLUUID getAOFolder();
@@ -116,7 +116,7 @@ class AOEngine
void checkBelowWater(BOOL yes);
BOOL importNotecard(const LLInventoryItem* item);
- void processImport();
+ void processImport( bool );
BOOL swapWithPrevious(AOSet::AOState* state,S32 index);
BOOL swapWithNext(AOSet::AOState* state,S32 index);
@@ -188,10 +188,12 @@ class AOEngine
LLUUID mLastOverriddenMotion;
std::vector mSets;
+ std::vector mOldSets;
AOSet* mCurrentSet;
AOSet* mDefaultSet;
AOSet* mImportSet;
+ std::vector mOldImportSets;
LLUUID mImportCategory;
S32 mImportRetryCount;
};