New API: Animation/Timeline improvement

John Hendrikx john.hendrikx at gmail.com
Tue Dec 19 00:14:38 UTC 2023


I don't think that's the correct solution, as it opens up a whole new 
avenue for subtle bugs, bringing GC/JVM/OS whims and settings into the 
mix. We want the clean-up to be easier to reason about, not harder.

Even if it were a good idea, it's likely also going to be a breaking 
change to add weak references at this stage, without some kind of opt-in 
(which would then require new API, in which case we might as well go for 
a solution that has no need of weak references).

I feel I have to keep repeating this, but there almost zero guarantees 
made by the JVM or Java with regards to references; they are fine for 
internal processes carefully designed to have no user visible side 
effects, but burdening the user with side effects (delayed clean-up, or 
early unexpected clean-up due to lack of a hard reference) without the 
user actually choosing to use a reference type themselves is not a good 
design.

--John

On 18/12/2023 17:18, Andy Goryachev wrote:
>
> Would making Timeline to use WeakReferences solve the issue without 
> the need for a new API?
>
> -andy
>
> *From: *openjfx-dev <openjfx-dev-retn at openjdk.org> on behalf of John 
> Hendrikx <john.hendrikx at gmail.com>
> *Date: *Friday, December 15, 2023 at 21:53
> *To: *openjfx-dev <openjfx-dev at openjdk.org>
> *Subject: *New API: Animation/Timeline improvement
>
> Hi list,
>
> I've noticed that Animations and Timelines are often a source of leaks,
> and their clean-up is often either non-existent or incorrect.  The
> reason they cause leaks easily is because a running animation or
> timeline is globally referred from a singleton PrimaryTimer.  The
> animation or timeline then refers to properties or event handlers which
> refer to controls (which refer to parents and the entire scene).
>
> For example:
>
> - ScrollBarBehavior uses a Timeline, but neglects to clean it up.  If it
> was running at the time a Scene is detached from a Window, and that
> Scene is left to go out of scope, it won't because Timeline refers it;
> this can happen if the behavior never receives a key released event.
>
> - ScrollBarBehavior has no dispose method overridden, so swapping Skins
> while the animation is running will leave a Timeline active (it uses
> Animation.INDEFINITE)
>
> - SpinnerBehavior has flawed clean up; it attaches a Scene listener and
> disables its timeline when the scene changed, but the scene doesn't have
> to change for it to go out of scope as a whole... Result is that if you
> have a spinner timeline running, and you close the entire window (no
> Scene change happens), the entire Scene will still be referred.  It also
> uses an indefinite cycle count.  It also lacks a dispose method, so
> swapping Skins at a bad moment can also leave a reference.
>
> I think these mistakes are common, and far too easy to make.  The most
> common use cases for animations revolve around modifying properties on
> visible controls, and although animations can be used for purposes other
> than animating UI controls, this is extremely rare. So it is safe to
> say that in 99% of cases you want the animation to stop once a some Node
> is no longer showing. For both the mentioned buggy behaviors above, this
> would be perfect.  A spinner stops spinning when no longer showing, and
> a scroll bar stops scrolling when no longer showing. It is also likely
> to apply for many other uses of timelines and animations.
>
> I therefore want to propose a new API, either on Node or Animation (or
> both):
>
>      /**
>       * Creates a new timeline which is stopped automatically when 
> this Node
>       * is no longer showing. Stopping timelines is essential as they
> may refer
>       * nodes even after they are no longer used anywhere, preventing
> them from
>       * being garbage collected.
>       */
>      Node.createTimeline();  // and variants with the various Timeline
> constructors
>
> And/or:
>
>      /**
>       * Links this Animation to the given Node, and stops the animation
>       * automatically when the Node is no longer showing. Stopping
> animations
>       * is essential as they may refer nodes even after they are no
> longer used
>       * anywhere, preventing them from being garbage collected.
>       */
>      void stopWhenHidden(Node node);
>
> The above API for Animation could also be provided through another
> constructor, which takes a Node which will it be linked to.
>
> Alternatives:
>
> - Be a lot more diligent about cleaning up animations and timelines
> (essentially maintain the status quo which has led to above bugs)
> - Use this lengthy code fragment below:
>
>      Timeline timeline = new Timeline();
>
>      someNode.sceneProperty()
>          .when(timeline.statusProperty().map(status -> status !=
> Status.STOPPED))
>          .flatMap(Scene::windowProperty)
>          .flatMap(Window::showingProperty)
>          .orElse(false)
>          .subscribe(showing -> {
>              if (!showing) timeline.stop();
>          });
>
> The `when` line ensures that the opposite problem (Nodes forever
> referencing Timelines) doesn't occur if you are creating a new Timeline
> for each use (not recommended, but nonetheless a common occurrence).
>
> --John
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20231219/944df4ef/attachment-0001.htm>


More information about the openjfx-dev mailing list