<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Aptos;
        panose-1:2 11 0 4 2 2 2 2 2 4;}
@font-face
        {font-family:"Iosevka Fixed SS16";
        panose-1:2 0 5 9 3 0 0 0 0 4;}
@font-face
        {font-family:"Times New Roman \(Body CS\)";
        panose-1:2 11 6 4 2 2 2 2 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        font-size:10.0pt;
        font-family:"Aptos",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Iosevka Fixed SS16";
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;
        mso-ligatures:none;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></style>
</head>
<body lang="EN-US" link="blue" vlink="purple" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Even though JavaFX explicitly permits creating Nodes and Scenes in a thread other than the Application Thread, I think it is still a bad idea, and I would strongly suggest
 against doing so.  The code might work - initially - but you will soon discover that it presents a constant source of issues, especially after the application is deployed.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">I would also question the value of such a design.  How many milliseconds is being saved by trying to instantiate Nodes in a background thread?  If you create only a few objects,
 there is absolutely no benefit (and a huge maintenance burden), but if there are too many objects created then maybe one is doing something wrong, perhaps instead one should try to create things in batches?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">So my recommendation would remain the same: please don't.  Always access JavaFX objects from the Application Thread.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<div id="mail-editor-reference-message-container">
<div>
<div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal" style="margin-bottom:12.0pt"><b><span style="font-size:12.0pt;color:black">From:
</span></b><span style="font-size:12.0pt;color:black">openjfx-dev <openjfx-dev-retn@openjdk.org> on behalf of Kevin Rushforth <kevin.rushforth@oracle.com><br>
<b>Date: </b>Wednesday, April 23, 2025 at 11:41<br>
<b>To: </b>openjfx-dev@openjdk.org <openjfx-dev@openjdk.org><br>
<b>Subject: </b>Re: ExpressionHelper thread-safety<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-size:11.0pt">This came up most recently in the discussion of
<br>
<a href="https://github.com/openjdk/jfx/pull/1697">https://github.com/openjdk/jfx/pull/1697</a><br>
<br>
As noted by you and in that PR, properties are not thread-safe. If two <br>
threads add a listener concurrently, or if one thread adds a listener <br>
while and another thread notifies the listeners, it is likely to fail.<br>
<br>
So the question is: Is it worth doing something about this? And if so, <br>
how far do we go?<br>
<br>
Making the add/remove listeners operations on ExpressionHelper (and <br>
related classes?) thread-safe so that listeners could be added or <br>
removed on any thread concurrently with each other and with the <br>
operation off firing a listener probably wouldn't be too hard or have <br>
much downside (the performance impact should be negligible and it is <br>
unlikely to cause a deadlock).<br>
<br>
You still wouldn't be able to modify a property on more than one thread, <br>
nor control the thread on which listeners are notified (they are <br>
notified on the thread that mutates the property), so it won't magically <br>
solve all your threading issues; and you still would need to deal with <br>
the fact that your listener can be called on a different thread than the <br>
one which added it.<br>
<br>
I'd like to hear from Andy, John, and others as to whether they think <br>
there is value in providing partial thread-safety for the add/remove <br>
listener methods of properties.<br>
<br>
-- Kevin<br>
<br>
<br>
On 4/23/2025 9:58 AM, Christopher Schnick wrote:<br>
> Hello,<br>
><br>
> I encountered a rare exception where adding listeners to an observable <br>
> value might break when they are added concurrently. This is due to <br>
> ExpressionHelper not being synchronized. I thought about how to fix <br>
> this on my side, but it is very difficult to do. As this is not a <br>
> typical platform thread issue, in my opinion it should be possible to <br>
> add listeners to one observable value from any thread without having <br>
> to think about any potential synchronization issues (which I can't <br>
> solve other than just running everything on one thread).<br>
><br>
> Even worse, due to the size and array being two different variables <br>
> and being incremented unsafely, once such a concurrent modification <br>
> occurs, this invalid state will persist permanently and will cause <br>
> exceptions on any further method call as well. The only solution is to <br>
> restart the application.<br>
><br>
> This is how a stack trace looks like when this occurs:<br>
><br>
> 21:25:38:840 - error: Index 2 out of bounds for length 2<br>
> java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for <br>
> length 2<br>
>     at <br>
> com.sun.javafx.binding.ExpressionHelper$Generic.addListener(ExpressionHelper.java:248)<br>
>     at <br>
> com.sun.javafx.binding.ExpressionHelper$Generic.addListener(ExpressionHelper.java:200)<br>
>     at <br>
> com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:65)<br>
>     at <br>
> javafx.beans.binding.ObjectBinding.addListener(ObjectBinding.java:86)<br>
>     at javafx.beans.binding.StringBinding.bind(StringBinding.java:114)<br>
>     at javafx.beans.binding.Bindings$7.<init>(Bindings.java:428)<br>
>     at <br>
> javafx.beans.binding.Bindings.createStringBinding(Bindings.java:426)<br>
>     at <br>
> io.xpipe.app.util.StoreStateFormat.shellEnvironment(StoreStateFormat.java:24)<br>
>     at <br>
> io.xpipe.ext.proc.env.ShellEnvironmentStoreProvider.informationString(ShellEnvironmentStoreProvider.java:155)<br>
>     at <br>
> io.xpipe.app.comp.store.StoreEntryWrapper.update(StoreEntryWrapper.java:228)<br>
>     at <br>
> io.xpipe.app.comp.store.StoreViewState.lambda$updateContent$1(StoreViewState.java:147)<br>
>     at java.lang.Iterable.forEach(Iterable.java:75)<br>
>     at <br>
> io.xpipe.app.comp.store.StoreViewState.updateContent(StoreViewState.java:147)<br>
>     at <br>
> io.xpipe.app.comp.store.StoreViewState.init(StoreViewState.java:93)<br>
>     at <br>
> io.xpipe.app.core.mode.BaseMode.lambda$onSwitchTo$1(BaseMode.java:109)<br>
>     at io.xpipe.app.util.ThreadHelper.lambda$load$0(ThreadHelper.java:78)<br>
>     at java.lang.Thread.run(Thread.java:1447)<br>
><br>
> 21:25:38:847 - error: Index 3 out of bounds for length 2<br>
> java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for <br>
> length 2<br>
>     at <br>
> com.sun.javafx.binding.ExpressionHelper$Generic.addListener(ExpressionHelper.java:248)<br>
>     at <br>
> com.sun.javafx.binding.ExpressionHelper$Generic.addListener(ExpressionHelper.java:200)<br>
>     at <br>
> com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:65)<br>
>     at <br>
> javafx.beans.binding.ObjectBinding.addListener(ObjectBinding.java:86)<br>
>     at javafx.beans.binding.StringBinding.bind(StringBinding.java:114)<br>
>     at javafx.beans.binding.Bindings$7.<init>(Bindings.java:428)<br>
>     at <br>
> javafx.beans.binding.Bindings.createStringBinding(Bindings.java:426)<br>
>     at <br>
> io.xpipe.app.util.StoreStateFormat.shellEnvironment(StoreStateFormat.java:24)<br>
>     at <br>
> io.xpipe.ext.proc.env.ShellEnvironmentStoreProvider.informationString(ShellEnvironmentStoreProvider.java:155)<br>
>     at <br>
> io.xpipe.app.comp.store.StoreEntryWrapper.update(StoreEntryWrapper.java:228)<br>
>     at <br>
> io.xpipe.app.comp.store.StoreEntryWrapper.lambda$setupListeners$3(StoreEntryWrapper.java:143)<br>
>     at <br>
> io.xpipe.app.util.PlatformThread.lambda$runLaterIfNeeded$0(PlatformThread.java:318)<br>
>     at <br>
> com.sun.javafx.application.PlatformImpl.lambda$runLater$4(PlatformImpl.java:424)<br>
>     at <br>
> com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture(InvokeLaterDispatcher.java:95)<br>
>     at <br>
> com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java)<br>
><br>
> This full log goes up to index 50 out of bounds due to the recurring <br>
> nature of this exception.<br>
><br>
> Looking at the implementation of ExpressionHelper, I don't see any <br>
> harm in just synchronizing the methods, at least from my perspective. <br>
> But I guess that is up to the developers to decide. The only real <br>
> solution I have as an application developer is to perform all <br>
> initialization on one thread or just hope that this error is rare <br>
> enough, both of which aren't great options. So I hope that a potential <br>
> synchronization of the ExpressionHelper methods can be considered.<br>
><br>
> Best<br>
> Christopher Schnick<br>
><o:p></o:p></span></p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>