Crash [with patch] during exit when fading out Drawable

short version:

The AnimationTask owns the proxy via a unique_ptr but the DrawableComposite deletes all children - including the proxy - in its destructor.

longer version:

  1. ComponentAnimator::fadeOut() creates an AnimationTask with useProxyComponent = true
  2. The proxy is a std::unique_ptr
  3. the proxy is added as a sibling to the animated component. The proxy may now be a child of a DrawableComposite.
  4. DrawableComposite calls deleteAllChildren() in its destructor
  5. the destructor of Desktop calls animator.cancelAllAnimations() which deletes all AnimationTasks and with it the proxy hold via a unique_ptr. But the proxy was already deleted by deleteAllChildren()

reproduction:

  1. load SVG into a (owned) member of your component
  2. call Desktop::getInstance().getAnimator().fadeOut(someChildOfTheSvg, 1000 * 1000);
  3. quit the application via cmd+q

possible solution:

  1. do not delete the ProxyComponent in DrawableComposite
  2. do not delete the proxy if it was deleted elsewhere. This can be accomplished by using a Component::SafePointer instead of a unique_ptr. A patch for this is attached.

Patch:

--- a/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp
+++ b/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp
@@ -32,6 +32,11 @@ class ComponentAnimator::AnimationTask
 public:
     AnimationTask (Component* c) noexcept  : component (c) {}
 
+    ~AnimationTask()
+    {
+        proxy.deleteAndZero();
+    }
+
     void reset (const Rectangle<int>& finalBounds,
                 float finalAlpha,
                 int millisecondsToSpendMoving,
@@ -58,17 +63,17 @@ public:
         midSpeed = invTotalDistance;
         endSpeed = jmax (0.0, endSpd * invTotalDistance);
 
+        proxy.deleteAndZero();
+
         if (useProxyComponent)
-            proxy.reset (new ProxyComponent (*component));
-        else
-            proxy.reset();
+            proxy = new ProxyComponent (*component);
 
         component->setVisible (! useProxyComponent);
     }
 
     bool useTimeslice (const int elapsed)
     {
-        if (auto* c = proxy != nullptr ? proxy.get()
+        if (auto* c = proxy != nullptr ? proxy.getComponent()
                                        : component.get())
         {
             msElapsed += elapsed;
@@ -167,6 +172,7 @@ public:
             toBehind (&c);
         }
 
+
         void paint (Graphics& g) override
         {
             g.setOpacity (1.0f);
@@ -181,7 +187,7 @@ public:
     };
 
     WeakReference<Component> component;
-    std::unique_ptr<Component> proxy;
+    Component::SafePointer<Component> proxy;
 
     Rectangle<int> destination;
     double destAlpha;

Added in 971fc8e - thanks for reporting!

1 Like