View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0002317 | NoesisGUI | C++ SDK | public | 2022-03-31 20:22 | 2022-05-03 19:15 |
Reporter | steveh | Assigned To | sfernandez | ||
Priority | normal | Severity | crash | Reproducibility | sometimes |
Status | resolved | Resolution | fixed | ||
Product Version | 3.1.4 | ||||
Target Version | 3.1.5 | Fixed in Version | 3.1.5 | ||
Summary | 0002317: Crash when applying animation values in TimeManager::Tick | ||||
Description | Hi guys, I've spent quite a bit of time tracking down a crash. I've tracked it down to attempting to apply an animation value on a destroyed dependency object. I've created a reproducible sample that I've attached as a ZIP to this bug report. So what has happened? 1. In TimeManager::Tick it collates together all the animation values that it needs to apply to all dependency objects this frame. This is stored in a local HashMap on the stack which contains non-ref counted pointers 2. It then iterates through each AnimationValue and call DependencyObject::SetAnimation passing in the new value. 3. This invoked dependency property changed events. 4. If user code binds to this (e.g. the IsVisibleChanged event) we can run code which ultimately removes all references to dependency object and removes it 5. This can end up freeing the dependency objects that are currently stored as raw addresses in the stack that was created in step 1. 6. When it attempts to apply the animation value on the now deleted object it crashes. So just to clarify, it's this bit of code in TimeManager::Tick which is broken: ----------------- // Update properties AnimationValues animationValues; for (Targets::Bucket& b: mTargets) { DependencyObject* target = b.key; const AnimatedProperties& properties = b.value.animatedProperties; EvaluateTarget(target, properties, animationValues, finishedClocks); } // Now that we are out of the targets loop, we can safely apply the new animation values ApplyAnimationValues(animationValues); ---------------- So if dependency objects that currently reside in the animationValues map are removed during ApplyAnimationValues it can crash. In my case this was caused by changing the Content from a DependencyPropertyChangedEvent which cleared out the ContentTemplate which destroyed the DependencyObjects created by that DataTemplate. Since this happened instantly, it ended up invalidating the AnimationValue map which had already taken a raw pointer to the DependencyObjects. I've fixed this locally by adding all targets in the AnimationValue map to the "aliveTargets" vector: for (Targets::Bucket& b: mTargets) { DependencyObject* target = b.key; const AnimatedProperties& properties = b.value.animatedProperties; EvaluateTarget(target, properties, animationValues, finishedClocks); // Keep this target alive as it could be invalidated in ApplyAnimationValues aliveTargets.EmplaceBack().Reset(target); // [Sumo SPH 2022-03-31] - Keep these animation targets alive! } I'm not sure if this is the best fix, but I've not been able to crash it after this change. Cheers! | ||||
Steps To Reproduce | 1. Download my sample. 2. Extract to Noesis SDK 3. Build it 4. Click the button. When the button is collapsed it will trigger a DependencyPropertyChangedEvent which will lead to a crash when the content is changed. Note: This does not seem to be 100% for some reason. If it doesn't break, just restart the sample. It'll break eventually. | ||||
Tags | No tags attached. | ||||
Platform | Any | ||||
Thanks for the report Steve, giving high-priority to this | |
Hi guys, so in addition to the above change, one of our tech directors is investigating other stomps with other tools and has identified the following stomp (I've attached an image). The change is the P4 diff. Unfortunately I didn't investigate this stomp so I don't have any more info on it. It looks like erasing the clock pointer inside RemoveMasterClock, and then we try to write mTarget to 0 on a now-erased clock. The fix is to zero out the target on the clock before it's erased from the mMasterClocks vector. |
|
That change is not correct because there is another use of the RemoveMasterClock (in the method OnClockFinished) that requires later use of clock->mTarget. The easiest way to fix it, before I analyze this better is to keep a reference to the clock in RemoveUnusedMasterClocks: void TimeManager::RemoveUnusedMasterClocks(const ClocksSet& clocks) { for (Clock* masterClock: clocks) { if (!IsClockUsed(masterClock)) { Ptr<Clock> masterClockPtr(masterClock); RemoveMasterClock(masterClock); masterClock->mTarget = 0; } } } Could you try that? |
|
Hi Sergio, your suggestion works great. We've submitted that to our local branch, much appreciated. | |
Both issues solved in changeset r11317. | |
Date Modified | Username | Field | Change |
---|---|---|---|
2022-03-31 20:22 | steveh | New Issue | |
2022-03-31 20:22 | steveh | File Added: AnimationCrashBug.zip | |
2022-04-04 07:53 | jsantos | Assigned To | => sfernandez |
2022-04-04 07:53 | jsantos | Status | new => assigned |
2022-04-04 07:53 | jsantos | Product Version | => 3.1.4 |
2022-04-04 07:53 | jsantos | Target Version | => 3.1.5 |
2022-04-04 07:54 | jsantos | Note Added: 0007884 | |
2022-04-22 14:37 | steveh | Note Added: 0007906 | |
2022-04-22 14:37 | steveh | File Added: image.png | |
2022-04-22 16:18 | sfernandez | Status | assigned => feedback |
2022-04-22 16:18 | sfernandez | Note Added: 0007907 | |
2022-04-22 18:30 | steveh | Note Added: 0007909 | |
2022-04-22 18:30 | steveh | Status | feedback => assigned |
2022-05-03 19:15 | sfernandez | Status | assigned => resolved |
2022-05-03 19:15 | sfernandez | Resolution | open => fixed |
2022-05-03 19:15 | sfernandez | Fixed in Version | => 3.1.5 |
2022-05-03 19:15 | sfernandez | Note Added: 0007916 |