1.7.2 EventManager crashes
by James Ford · in Torque Game Builder · 05/03/2008 (1:52 pm) · 20 replies
I didn't realize this handy EventManager simobject class existed until today. Was it added recently? It looks similar to the MessageRouter resource - just curious which came first.
Anyhow, I got a couple crashes while using it.
If you create an EventManager object and try registering or posting an event without specifying the "queue" field - crash. A descriptive console error would be nice instead.
If you try to remove a subscriber to an event while in the event's callback ( eg. you only need to handle the event once ) - crash. This seems to mess up the iterator when it gets back in C++. I worked around this by scheduling the call to remove instead of doing it immediately. Still - this could allow the event to be received more than once - and that may be exactly what you are trying to prevent ( by unsubscribed during the event callback ). If the EventManager gracefully handled this case it would be much appreciated.
Anyhow, I got a couple crashes while using it.
If you create an EventManager object and try registering or posting an event without specifying the "queue" field - crash. A descriptive console error would be nice instead.
If you try to remove a subscriber to an event while in the event's callback ( eg. you only need to handle the event once ) - crash. This seems to mess up the iterator when it gets back in C++. I worked around this by scheduling the call to remove instead of doing it immediately. Still - this could allow the event to be received more than once - and that may be exactly what you are trying to prevent ( by unsubscribed during the event callback ). If the EventManager gracefully handled this case it would be much appreciated.
About the author
http://jamesdev.info
#2
On issue #2,
I am not actually deleting the subscribed object ( although that would probably crash too ). It was de-subscribing ( EventManager::remove ) an object from an event type ( while in an event callback ) that caused a crash.
I debugged the crash a bit and it is not due to a "core TorqueScript compiler issue" ... it is just an issue in the EventManager code itself. Specifically the code that calls event callbacks and the .remove() code do not play nicely with each other.
Doing this really shouldn't cause a crash, its just an oversight.
Just as an idea ... keep a bool in the EventManager signifying if you are "in a callback" and automatically handle .remove(s) differently in that case.
05/12/2008 (9:49 am)
Thanks Stephen.On issue #2,
I am not actually deleting the subscribed object ( although that would probably crash too ). It was de-subscribing ( EventManager::remove ) an object from an event type ( while in an event callback ) that caused a crash.
I debugged the crash a bit and it is not due to a "core TorqueScript compiler issue" ... it is just an issue in the EventManager code itself. Specifically the code that calls event callbacks and the .remove() code do not play nicely with each other.
Doing this really shouldn't cause a crash, its just an oversight.
Just as an idea ... keep a bool in the EventManager signifying if you are "in a callback" and automatically handle .remove(s) differently in that case.
#3
07/13/2008 (10:43 pm)
Has there been any movement on this bug? I found this around the same time, and it would probably be fairly useful in certain situations. Certainly better than constantly polling. Thanks.
#4
07/14/2008 (10:00 am)
Although I did run into crashes in the specific curtumstances I mentioned, the EventManager is very usable, and a good alternative to constant polling.
#5
07/15/2008 (7:06 am)
Ahh yes. Right you are James. I simply specified the name of the queue object and it works. I actually assumed (and you know what they say about that) that the queue was not accessible via script. Thanks!
#6
Also, in 1.7.4 the console function "isRegisteredEvent" was actually calling "registerEvent". Seems odd to me.
07/15/2008 (11:04 am)
Actually, James, are you using the EventManager with behaviors? I am having a problem where certain event won't trigger on certain objects. This seems to occure where that behavior type is subscribed to the same event multiple times, but on different objects in the scene. I don't immediately see why it should be a problem, as BehaviorTemplates are simply another type of SimObject according to the documentation.Also, in 1.7.4 the console function "isRegisteredEvent" was actually calling "registerEvent". Seems odd to me.
#7
07/15/2008 (11:14 am)
I wasn't using it with behaviors. Are you registering the BehaviorTemplate or the BehaviorInstance to the event? You would definitely want to register the BehaviorInstance not the Template, or if thats not possibly, register the object owning the behavior and have the object call a callback on the behavior when it gets the event.
#8
07/15/2008 (11:28 am)
It's the behavior instance as I am subscribing to the event when the behavior instances onLevelLoaded is called, not in the template declaration. It's odd, because if I simply pass in %this.owner it will call the methods on the behavior instance fine, but I strongly doubt that this is the correct or reliable way to go about it. Hmm.
#9
07/15/2008 (11:55 am)
I'm not sure whats really going on there, but it doesn't sound that bad to register the owner. If you unregister the owner when the behavior is removed then its essentially the same as registering the behavior.
#10
07/15/2008 (11:41 pm)
I'd really like to have a more in depth look at in the source but I'm afraid I don't have the time. So I think for now what I am likely to do is simply bind those objects that wish to subscribe to the event to a class/namespace then call the required behavior function from there, at least then I know exactly what it is doing. I don't really understand by what mechanism behavior methods would get call from their owner objects at this point.
#11
As long as you don't have other namespaces ( eg. classes or behaviors ) on the same object with the behavior defining methods of the same name you should be ok.
07/16/2008 (9:32 am)
That is a normal "behavior" with objects and behaviors. If an object foo has a behavior that implements a method bar "food.bar()" will automatically call that method on the behavior. I haven't actually looked at the source code where this is done either. But I assume it first tries all the object's namespaces and if it doesn't find the method it then looks in any attached behaviors.As long as you don't have other namespaces ( eg. classes or behaviors ) on the same object with the behavior defining methods of the same name you should be ok.
#12
I was experiencing some of the problems mentioned before, for isntance if one dinosaur is hunting another dinosaur, it has a HUNT behavior object, which is listening to the prey object for positional changes. If the position changes, I restart the behavior, destroying the old one (after a 0 tick schedule) and restarting. Eventually, it crashes with a junky callstack.
Has anyone else seen this problem and been able to fix it?
03/05/2009 (12:25 pm)
I'm having the same crash in TGEA 1.7.1. I'm wondering if it's related to the fact that I've got many event managers (one for each entity) and quite a few listeners as well, all constructed and destructed on the fly. I am pretty sure I'm doing everything cleanly.I was experiencing some of the problems mentioned before, for isntance if one dinosaur is hunting another dinosaur, it has a HUNT behavior object, which is listening to the prey object for positional changes. If the position changes, I restart the behavior, destroying the old one (after a 0 tick schedule) and restarting. Eventually, it crashes with a junky callstack.
Has anyone else seen this problem and been able to fix it?
#13
Well having messed with the Event stuff in quite a while, but it sounds like that schedule 0 trick is pretty much what I was doing (to avoid deleting/unsubscribing an object immediately during the event-callback).
So, it could be something else entirely, 'but'...
Deleting/reallocating a behavior every time a position changes... that sounds like a general bad idea. So perhaps you could try refactoring it such that a 'reinit' method can be called and actual deletion/reallocation is not required, then as a side effect, this current issue might just go away...
03/05/2009 (1:44 pm)
Sure I've had lots of problems getting my dinosaurs to work '-) No really I'm serious...Well having messed with the Event stuff in quite a while, but it sounds like that schedule 0 trick is pretty much what I was doing (to avoid deleting/unsubscribing an object immediately during the event-callback).
So, it could be something else entirely, 'but'...
Deleting/reallocating a behavior every time a position changes... that sounds like a general bad idea. So perhaps you could try refactoring it such that a 'reinit' method can be called and actual deletion/reallocation is not required, then as a side effect, this current issue might just go away...
#14
03/05/2009 (5:22 pm)
James- Thanks for the reply. I'm actually not reallocing every time the position changes, just when their node changes (10 meters apart). It is ugly but it also should work :) It's also odd because I have another behavior that does almost exactly the same thing (an "Imprint" behavior for juveniles) that doesn't crash. Actually I'd like a lighter event manager, so I might just reimplement my own.
#15
(btw, I'm still prototyping, so optimizing the realloc issue was something I probably would have done down the road anyways)
03/06/2009 (11:36 am)
Well, I fixed it so it doesn't realloc anything and it still crashes :( I'll post more details when I know more.(btw, I'm still prototyping, so optimizing the realloc issue was something I probably would have done down the road anyways)
#16
I agree with the above poster that there should really be a safeguard in there to wait until it's outside the callstack of the call before actually removing the reference. Maybe just a dirty flag that prevents future calls to the event, and then upon completing the callback removes the event? The problem with this use case is that when you use it incorrectly, it trashes the callstack, making it very difficult to track down.
03/06/2009 (12:12 pm)
I spoke too soon -- in fixing the realloc problem I reintroduced the problem where the callback was removing the eventlistener. Once I fixed that everything seems to work.I agree with the above poster that there should really be a safeguard in there to wait until it's outside the callstack of the call before actually removing the reference. Maybe just a dirty flag that prevents future calls to the event, and then upon completing the callback removes the event? The problem with this use case is that when you use it incorrectly, it trashes the callstack, making it very difficult to track down.
#17
05/08/2009 (4:17 pm)
Just an update on this, I wrote a counter that prevents new calls to the same callback once the callback has been removed. Once the counter reaches 0 (the callstack unwinds), the event actually gets removed. It works just fine. It's still theorectically possible to have logical errors with this -- if you try to re-add the event listener while inside the stack frame I think it will cause errors, other than that, this fix is beeter than nothing. Without it the EventManager was almost unusable for me.
#18
05/08/2009 (5:35 pm)
Hmm, if you feel like sending me that fix maybe I can put it into t3d since I happen to be looking at it right now!
#19
05/09/2009 (9:44 am)
Sent them to you via email!
#20
05/11/2009 (1:08 am)
Got it, thanks
Torque 3D Owner Stephen Zepp
Noted, and agreed. Logged as TGB-85.
You may be right in very unusual circumstances, but a schedule with a delay of 0 should alleviate all concerns regarding getting the callback twice.