Crash in SimGroup::onRemove()
by Stephane Conde · in Torque Game Engine · 10/04/2006 (7:05 pm) · 2 replies
I am getting a crash in SimGroup::onRemove if I quit a mission while it is loading (just before being dropped into the mission). It does not crash reliably (unfortunately) but often enough to be repeated. I am pretty stumped with this one and thought I would throw it out to the community in the hopes someone has come across this issue or has some ideas...
The line in bold is the actual line it is crashing on inside of SimGroup::onRemove:
It seems like the *ptr SimObject has already been deleted (all of the values inside of it are garbage at the point it crashes) but the reference to it has not been removed from the SimGroup as it still iterates over it...
The game crashes while trying to delete the MissionCleanup SimGroup in the endMission() function.
It seems like something inside of the MissionCleanup SimGroup was deleted (apparently improperly) but was then never actually removed from the MissionCleanup SimGroup...
I suspect that this is something specific to my project and not to Torque itself (that's why it's not in the bugs forum), but would anyone know why something like that might happen?
On a hunch I checked to make sure that all onRemove functions in all children of SimObject were calling Parent::onRemove since SimObject::onRemove is responsible for removing a SimObject from a SimGroup... everything looked kosher.
Any ideas, thoughts, conjectures, posits, suggestions and of course solutions would be more than welcome. Thank you in advance for your time.
Stephane
Edit: Changed some wording to make things a little more clear
The line in bold is the actual line it is crashing on inside of SimGroup::onRemove:
void SimGroup::onRemove()
{
objectList.sortId();
if (objectList.size())
{
// This backwards iterator loop doesn't work if the
// list is empty, check the size first.
for (SimObjectList::iterator ptr = objectList.end() - 1; ptr >= objectList.begin(); ptr--)
{
[b](*ptr)->onGroupRemove();[/b]
(*ptr)->mGroup = NULL;
(*ptr)->unregisterObject();
(*ptr)->mGroup = this;
}
}
// Skip over our parent (SimSet) and go straight to SimObject
SimObject::onRemove();
}It seems like the *ptr SimObject has already been deleted (all of the values inside of it are garbage at the point it crashes) but the reference to it has not been removed from the SimGroup as it still iterates over it...
The game crashes while trying to delete the MissionCleanup SimGroup in the endMission() function.
It seems like something inside of the MissionCleanup SimGroup was deleted (apparently improperly) but was then never actually removed from the MissionCleanup SimGroup...
I suspect that this is something specific to my project and not to Torque itself (that's why it's not in the bugs forum), but would anyone know why something like that might happen?
On a hunch I checked to make sure that all onRemove functions in all children of SimObject were calling Parent::onRemove since SimObject::onRemove is responsible for removing a SimObject from a SimGroup... everything looked kosher.
Any ideas, thoughts, conjectures, posits, suggestions and of course solutions would be more than welcome. Thank you in advance for your time.
Stephane
Edit: Changed some wording to make things a little more clear
About the author
Recent Threads
#2
10/14/2006 (10:29 pm)
Thanks for the thorough write, Stephane (and for mailing me to make sure I see this), I've forwarded it to the appropriate folk to get it reviewed and integrated!
Torque 3D Owner Stephane Conde
I had added some code which I had found in the latest version of TSE which looked like it finally dealt with the issue of ParticleEmitters not being properly cleaned up when a mission ended...
void ParticleEmitter::deleteWhenEmpty() { SimGroup *cleanup = dynamic_cast<SimGroup *>( Sim::findObject( "MissionCleanup") ); if( cleanup != NULL ) { cleanup->addObject( this ); } else { Con::errorf( "No mission cleanup group?" ); // if no mission cleanup group then it's probably been removed in shutdown deleteObject(); } mDeleteWhenEmpty = true; }The code looked fairly benign, but was in fact the root cause of the above problems. As soon as I added the code, I started experiencing problems in the SimGroup destructor... it was complaining about SimObjects being removed without their onRemove functions having been called. A quick glance at SimGroup::~SimGroup showed that any SimObjects that remained in the SimGroup were just being directly 'delete'd... I thought I was being smart and got the SimGroup desctructor to instead do things 'properly' and use deleteObject() on the remaining SimObjects so that their onRemove function would be properly called. This seemed to fix things... on the surface... but didn't get to the root of the problem.
The problem with the above code is that when the MissionCleanup group gets deleted it deletes all of the objects within it... So, when it reaches a ParticleEmitterNode, for example, it will delete it which makes the ParticleEmitterNode call ParticleEmitter::deleteWhenEmpty on the ParticleEmitter it owns which adds the ParticleEmitter to the MissionCleanup group (which has now already begun its removal process)... Needless to say, adding an object to a Group that has already begun its removal process is apparently not good...
So, all that to say that the above code is on the right track but missing a simple check:
void ParticleEmitter::deleteWhenEmpty() { SimGroup* cleanup = dynamic_cast<SimGroup *>( Sim::findObject( "MissionCleanup") ); if (cleanup != NULL && [b]!cleanup->isRemoved()[/b]) cleanup->addObject( this ); else { Con::errorf( "No mission cleanup group?" ); // if no mission cleanup group then it's probably been removed in shutdown deleteObject(); } mDeleteWhenEmpty = true; }I actually added a second check just because I'm kind of paranoid like that...
void ParticleEmitter::deleteWhenEmpty() { [b]if (!mDeleteWhenEmpty) {[/b] SimGroup* cleanup = dynamic_cast<SimGroup *>( Sim::findObject( "MissionCleanup") ); if (cleanup != NULL && [b]!cleanup->isRemoved()[/b]) cleanup->addObject( this ); else { Con::errorf( "No mission cleanup group?" ); // if no mission cleanup group then it's probably been removed in shutdown deleteObject(); } mDeleteWhenEmpty = true; [b]}[/b] }The above check ensures that we don't attempt to delete this ParticleEmitter more than once.
Now... all that being said, I would actually like to propose a more concrete fix than this: It seems like adding an object to a group that has already been marked as Removed would ALWAYS be a bad thing. Should this check not be added to the SimGroup::addObject() function? Maybe also the SimSet::addObject() function too? Then, in the ParticleEmitter::deleteWhenEmpty() function we could just check if the addObject() call was successful and, if not, just delete the Emitter right there. Come to think of it, an Assert would probably be the best method, since we really shouldn't be adding anything to any SimGroups that have been marked as Removed.
Hope this helps someone in the same position as me!
Cheers,
Stephane