<div dir="ltr">These are the options I see as reasonable:<div><br></div><div>1. Document that these methods *must* be run on the FX thread and throw an exception otherwise. This leaves it to the responsibility of the user. However, this will cause the backwards compatibility problems that Jugen brought up. As a side note, we do this in other methods already, but I always questioned why let the developer do something illegal - if there's only one execution path, force it.</div><div>2. Document that these methods *are* run on the FX thread (the user doesn't need to do anything) and change the implementation to call runLater(...) internally unless they are already on the FX thread. This will be backwards compatible for the most part (see option 3). The downside might be some surprise when these methods behave differently.</div><div>3. Document that it's *recommended* that these methods be run on the FX thread and let the user be responsible for the threading. We can explain that manipulating nodes that are attached to an active scenegraph should be avoided.</div><div><br></div><div>I prefer option 2 over 1 regardless of the backwards compatibility issue even, but would like to know if I'm missing something here because in theory this change could be done to any "must run on the FX thread" method and I question why the user had the option to get an exception.</div><div>Option 3 is risky and I wager a guess that it will be used wrongly more often than not. It does allow some (what I would call) valid niche uses. I never did it.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jan 24, 2024 at 4:21 PM Kevin Rushforth <<a href="mailto:kevin.rushforth@oracle.com" target="_blank">kevin.rushforth@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>
<div>
<font face="Helvetica, Arial, sans-serif">I'd like to hear from the
others on this. I don't see any fundamental problem with having
the play/pause/stop methods wrap their implementation in a
runLater (if not on the FX Application thread already), and
documenting that it does so, if we can get general agreement.<br>
<br>
-- Kevin<br>
<br>
<br>
</font>
<div>On 1/24/2024 5:29 AM, Jurgen Doll
wrote:<br>
</div>
<blockquote type="cite">
<div><big>Hi Kevin</big></div>
<div><big><br>
</big></div>
<div><big>If I may make one more final appeal then to an
alternative solution please.</big></div>
<div><big><br>
</big></div>
<div><big>Could we then instead of throwing an Exception rather
invoke runLater if needed inside play, stop, and resume.</big></div>
<div><big><br>
</big></div>
<div><big>Putting the onus on the developer is fine if it is the
developer that is invoking the call, but if it's in a library
then it's a no go.</big><big><br>
</big><big><br>
</big><big>In my application I have two libraries that I know of
where this happens. The major problem is that with FX22 as it
now stands my </big><big>application just crashes because
play() does an FX thread check and throws an Exception which
it never did before. There are bound </big><big>to be other
applications out there that are going to find themselves in a
similar position.</big></div>
<div><big><br>
</big></div>
<div><big>PLEASE !</big></div>
<div><big><br>
</big></div>
<div><big>Regards</big></div>
<div><big>Jurgen</big></div>
<div><big><br>
</big></div>
<div><br>
</div>
<div><br>
</div>
<div>On Wed, 24 Jan 2024 15:15:31 +0200, Kevin Rushforth
<a href="mailto:kevin.rushforth@oracle.com" target="_blank"><kevin.rushforth@oracle.com></a> wrote:<br>
</div>
<br>
<blockquote style="margin:0px 0px 0.8ex;border-left:2px solid rgb(0,0,255);padding-left:1ex">
Thank you to Jurgen for raising the question and to Nir, John,
and Michael for evaluating it.<br>
<br>
I conclude that there is insufficient motivation to revert the
change in behavior implemented by JDK-8159048 to allow calling
the play/pause/stop methods of Animation on a background thread.
Doing so without making it fully multi-thread-safe would be
asking for problems, and making it fully multi-thread-safe would
be a fair bit of work to do it right without a clear benefit.<br>
<br>
We will proceed with the current approach and let JDK-8159048
stand. Further, we will proceed with <a href="https://bugs.openjdk.org/browse/JDK-8324219" target="_blank">https://bugs.openjdk.org/browse/JDK-8324219</a>
which is under review in <a href="https://github.com/openjdk/jfx/pull/1342" target="_blank">https://github.com/openjdk/jfx/pull/1342</a><br>
<br>
-- Kevin<br>
<br>
<div>On 1/24/2024 12:30 AM, Nir Lisker
wrote:<br>
</div>
<blockquote type="cite">
<div dir="auto">After playing around with the code sample, I
think that this is not the right way to use the animation.
The reason is that there is no point in starting the
animation before the control is attached to the scenegraph,
or even made visible. A small refactoring where, e.g., the
controller class exposes a method to start the animation in
onSucceeded or just calls it on the FX thread is enough. I
never start an animation as part of the construction because
it's not the right time. John suggested tying the lifecycle
of the animation to the showing of the node, which also
solves the problem.
<div dir="auto"><br>
</div>
<div dir="auto">There are animations like PauseTransition or
other non-interfering Timelines that could reasonably be
run on a background thread. Or maybe just on an
unconnected control. This could be a reason to not limit
animation methods to the FX thread at the expense of
possible user errors, but document the pitfall.</div>
<div dir="auto"><br>
</div>
<div dir="auto">I don't see a good use case for modifying
controls in a background thread while still interacting
with the scenegraph, hence for adding multithread
support. </div>
<div dir="auto"><br>
</div>
<div dir="auto">- Nir</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Mon, Jan 22, 2024,
12:59 Jurgen Doll <<a href="mailto:javafx@ivoryemr.co.za" target="_blank">javafx@ivoryemr.co.za</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div>
<div><big>Here's an example as requested by Nir:</big></div>
<div><big><br>
</big></div>
<div><span style="color:rgb(127,0,85);font-weight:bold">public</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">class</span><span style="color:rgb(0,0,0)"> FxTimeLineTest </span><span style="color:rgb(127,0,85);font-weight:bold">extends</span><span style="color:rgb(0,0,0)"> Application</span></div>
<div>
<div style="background-color:rgb(255,255,255);padding:0px 2px">
<div style="color:rgb(0,0,0);background-color:rgb(255,255,255);font-family:Consolas;font-size:10pt;white-space:pre-wrap"><p style="margin:0px"><span style="color:rgb(0,0,0)">{</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">private</span><span style="color:rgb(0,0,0)"> BorderPane </span><span style="color:rgb(0,0,192)">bp</span><span style="color:rgb(0,0,0)"> = </span><span style="color:rgb(127,0,85);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> BorderPane( </span><span style="color:rgb(127,0,85);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> Label(</span><span style="color:rgb(42,0,255)">"Loading"</span><span style="color:rgb(0,0,0)">) );</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">public</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">static</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">void</span><span style="color:rgb(0,0,0)"> main( String[] </span><span style="color:rgb(106,62,62)">args</span><span style="color:rgb(0,0,0)"> ) {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(0,0,0);font-style:italic">launch</span><span style="color:rgb(0,0,0)">( FxTimeLineTest.</span><span style="color:rgb(127,0,85);font-weight:bold">class</span><span style="color:rgb(0,0,0)">, </span><span style="color:rgb(106,62,62)">args</span><span style="color:rgb(0,0,0)"> );</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(100,100,100)">@Override</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">public</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">void</span><span style="color:rgb(0,0,0)"> start( Stage </span><span style="color:rgb(106,62,62)">primaryStage</span><span style="color:rgb(0,0,0)"> ) </span><span style="color:rgb(127,0,85);font-weight:bold">throws</span><span style="color:rgb(0,0,0)"> Exception {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> Thread( </span><span style="color:rgb(127,0,85);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> LoadScene() ).start();</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">primaryStage</span><span style="color:rgb(0,0,0)">.setScene( </span>
<span style="color:rgb(127,0,85);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> Scene( </span><span style="color:rgb(0,0,192)">bp</span><span style="color:rgb(0,0,0)">, 300, 200 ) );</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">primaryStage</span><span style="color:rgb(0,0,0)">.setTitle( </span><span style="color:rgb(42,0,255)">"Memory Usage"</span><span style="color:rgb(0,0,0)"> );</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">primaryStage</span><span style="color:rgb(0,0,0)">.show();</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">private</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">class</span><span style="color:rgb(0,0,0)"> LoadScene </span><span style="color:rgb(127,0,85);font-weight:bold">extends</span><span style="color:rgb(0,0,0)"> Task<Parent> {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(100,100,100)">@Override</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">protected</span><span style="color:rgb(0,0,0)"> Parent call() </span><span style="color:rgb(127,0,85);font-weight:bold">throws</span><span style="color:rgb(0,0,0)"> Exception {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> Parent </span><span style="color:rgb(106,62,62)">p</span><span style="color:rgb(0,0,0)"> = FXMLLoader.</span><span style="color:rgb(0,0,0);font-style:italic">load</span><span style="color:rgb(0,0,0)">( getClass(
).getResource(</span><span style="color:rgb(42,0,255)">"TestView.fxml"</span><span style="color:rgb(0,0,0)">) );</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> Thread.</span><span style="color:rgb(0,0,0);font-style:italic">sleep</span><span style="color:rgb(0,0,0)">( 1000 );</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">return</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">p</span><span style="color:rgb(0,0,0)">;</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(100,100,100)">@</span><span style="color:rgb(100,100,100)">Override</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">protected</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">void</span><span style="color:rgb(0,0,0)"> succeeded() {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(0,0,192)">bp</span><span style="color:rgb(0,0,0)">.setCenter( getValue() );</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(100,100,100)">@</span><span style="color:rgb(100,100,100)">Override</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">protected</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">void</span><span style="color:rgb(0,0,0)"> failed() {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> getException().printStackTrace();</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">}
</span></p></div>
</div>
</div>
<div>
<div style="background-color:rgb(255,255,255);padding:0px 2px">
<div style="color:rgb(0,0,0);background-color:rgb(255,255,255);font-family:Consolas;font-size:10pt;white-space:pre-wrap"><p style="margin:0px">------------------------------------------------------------------------------------------------------</p></div>
</div>
</div>
<div><br>
</div>
<div>
<div style="background-color:rgb(255,255,255);padding:0px 2px">
<div style="color:rgb(0,0,0);background-color:rgb(255,255,255);font-family:Consolas;font-size:10pt;white-space:pre-wrap"><p style="margin:0px"><span style="color:rgb(127,0,85);font-weight:bold">public</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">class</span><span style="color:rgb(0,0,0)"> TestView</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">{</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(100,100,100)">@</span><span style="color:rgb(100,100,100)">FXML</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">private</span><span style="color:rgb(0,0,0)"> Label </span><span style="color:rgb(0,0,192)">memory</span><span style="color:rgb(0,0,0)">;</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">private</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">static</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">final</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">double</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(0,0,192);font-style:italic;font-weight:bold">MEGABYTE</span><span style="color:rgb(0,0,0)"> = 1024 * 1024;</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(100,100,100)">@</span><span style="color:rgb(100,100,100)">FXML</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">private</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">void</span><span style="color:rgb(0,0,0)"> initialize()</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">var</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">updater</span><span style="color:rgb(0,0,0)"> = </span><span style="color:rgb(127,0,85);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> Timeline</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> (</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> K
eyFrame( Duration.</span><span style="color:rgb(0,0,0);font-style:italic">seconds</span><span style="color:rgb(0,0,0)">(2.5), </span><span style="color:rgb(106,62,62)">event</span><span style="color:rgb(0,0,0)"> -></span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">var</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">runtime</span><span style="color:rgb(0,0,0)"> = Runtime.</span><span style="color:rgb(0,0,0);font-style:italic">getRuntime</span><span style="color:rgb(0,0,0)">();</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">double</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">maxMemory</span><span style="color:rgb(0,0,0)"> = </span><span style="color:rgb(106,62,62)">runtime</span><span style="color:rgb(0,0,0)">.maxMemory() / </span><span style="color:rgb(0,0,192);font-style:italic;font-weight:bold">MEGABYTE</span><span style="color:rgb(0,0,0)">;</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(127,0,85);font-weight:bold">double</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">usedMemory</span><span style="color:rgb(0,0,0)"> = (</span><span style="color:rgb(106,62,62)">runtime</span><span style="color:rgb(0,0,0)">.totalMemory() - </span><span style="color:rgb(106,62,62)">runtime</span><span style="color:rgb(0,0,0)">.freeMemory()) / </span><span style="color:rgb(0,0,192);font-style:italic;font-weight:bold">MEGABYTE</span><span style="color:rgb(0,0,0)">;</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(0,0,192)">memory</span><span style="color:rgb(0,0,0)">.setText( (</span><span style="color:rgb(127,0,85);font-weight:bold">int</span><span style="color:rgb(0,0,0)">) </span><span style="color:rgb(106,62,62)">usedMemory</span><span style="color:rgb(0,0,0)"> + </span><span>" MB / "</span><span style="color:rgb(0,0,0)"> + (</span><span style="color:rgb(127,0,85);font-weight:bold">int</span><span style="color:rgb(0,0,0)">) </span><span style="color:rgb(106,62,62)">maxMemory</span><span style="color:rgb(0,0,0)"> +</span><span style="color:rgb(42,0,255)">" MB"</span><span style="color:rgb(0,0,0)"> );</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> })</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> );</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">updater</span><span style="color:rgb(0,0,0)">.setCycleCount(Animation.</span><span style="color:rgb(0,0,192);font-style:italic;font-weight:bold">INDEFINITE</span><span style="color:rgb(0,0,0)">);
// This FXML is being loaded on a background thread
</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(106,62,62)">updater</span><span style="color:rgb(0,0,0)">.play();</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)"> }</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">}
</span></p></div>
</div>
</div>
<div>
<div style="background-color:rgb(255,255,255);padding:0px 2px">
<div style="color:rgb(0,0,0);background-color:rgb(255,255,255);font-family:Consolas;font-size:10pt;white-space:pre-wrap"><p style="margin:0px">------------------------------------------------------------------------------------------------------</p></div>
</div>
</div>
<div><br>
</div>
<div> <span style="color:rgb(0,0,0)"><big>TestView.fxml</big></span>
</div>
<div><br>
</div>
<div>
<div style="background-color:rgb(255,255,255);padding:0px 2px">
<div style="color:rgb(0,0,0);background-color:rgb(255,255,255);font-family:Consolas;font-size:10pt;white-space:pre-wrap"><div style="background-color:rgb(255,255,255);padding:0px 2px"><div style="color:rgb(0,0,0);background-color:rgb(255,255,255);font-family:Consolas;font-size:10pt;white-space:pre-wrap"><p style="margin:0px"><?xml version="1.0" encoding="UTF-8"?></p><p style="margin:0px">
</p><p style="margin:0px"><?import javafx.scene.control.Label?></p><p style="margin:0px"><?import javafx.scene.layout.StackPane?></p><p style="margin:0px">
</p><p style="margin:0px"><StackPane xmlns:fx="<a href="http://javafx.com/fxml/1" rel="noreferrer" target="_blank">http://javafx.com/fxml/1</a>" fx:controller="TestView"></p><p style="margin:0px"> <children></p><p style="margin:0px"> <Label fx:id="memory" text="Current / Max MB" ></p><p style="margin:0px"> <properties hashCode="12345" /></p><p style="margin:0px"> </Label></p><p style="margin:0px"> </children></p><p style="margin:0px"></StackPane></p></div></div></div>
</div>
</div>
<div><br>
</div>
<div><br>
</div>
<div><br>
</div>
<div>On Sat, 20 Jan 2024 17:08:41 +0200, Nir Lisker <<a href="mailto:nlisker@gmail.com" rel="noreferrer" target="_blank">nlisker@gmail.com</a>>
wrote:<br>
</div>
<br>
<blockquote style="margin:0px 0px 0.8ex;border-left:2px solid rgb(0,0,255);padding-left:1ex">
<div dir="ltr">Hi Jurgen,
<div><br>
</div>
<div>What I'm confused about the most is what it is
you are actually trying to do that
necessitates the use of animations outside of the
FX thread. You said that you need to initialize
controls on another thread, and that you are using
Task (both of which are fine), but how does
playing animations relate? Playing an animation is
something that is done explicitly, usually in
order to manipulate data. Can you give a real use
case, like a minimized version of what you're
doing?</div>
<div><br>
</div>
<div>- Nir</div>
</div>
</blockquote>
</div>
</blockquote>
</div>
</blockquote>
<br>
</blockquote>
<br>
<br>
<br>
<div id="m_-3605241258564149313m_2670470793887508609M2Signature">
<div>-- </div>
<div>Using Opera's mail client: <a href="http://www.opera.com/mail/" target="_blank">http://www.opera.com/mail/</a></div>
</div>
</blockquote>
<br>
</div>
</blockquote></div>