<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>For detecting long running tasks on the FX thread, there are some
      other options which you can do as a user (but perhaps we can
      support it directly within FX).  I use this kind of code to detect
      long running things on the FX thread:</p>
    <div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
      <div
style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p
      style="margin:0;"><span style="color:#000000;">    </span><span
      style="color:#3f5fbf;">/**</span></p><p style="margin:0;"><span
      style="color:#3f5fbf;">     * Adds a slow event warning whenever an event takes more than 10 </span><span
style="color:#3f5fbf;text-decoration:underline;text-decoration-color:#ff8040;text-decoration-style:wavy;">ms</span><span
      style="color:#3f5fbf;"> to process.  Note</span></p><p
      style="margin:0;"><span style="color:#3f5fbf;">     * that time spent in nested event loops cannot be properly taken into account as time</span></p><p
      style="margin:0;"><span style="color:#3f5fbf;">     * spent in nested event loops will be part of the event that triggered it giving false</span></p><p
      style="margin:0;"><span style="color:#3f5fbf;">     * positives.  In order for this time to be accurately reflected, the methods to enter</span></p><p
      style="margin:0;"><span style="color:#3f5fbf;">     * a nested event loop in this class should be used instead of the ones in </span><span
      style="color:#3f3fbf;">{@link Platform}</span><span
      style="color:#3f5fbf;">.</span></p><p style="margin:0;"><span
      style="color:#3f5fbf;">     *</span></p><p style="margin:0;"><span
      style="color:#3f5fbf;">     * </span><span
      style="color:#7f9fbf;font-weight:bold;">@param</span><span
      style="color:#3f5fbf;"> scene a Scene to which to add the slow event warning detection, cannot be null</span></p><p
      style="margin:0;"><span style="color:#3f5fbf;">     */</span></p><p
      style="margin:0;"><span style="color:#000000;">    </span><span
      style="color:#0000a0;font-weight:bold;">public</span><span
      style="color:#000000;"> </span><span
      style="color:#0000a0;font-weight:bold;">static</span><span
      style="color:#000000;"> </span><span
      style="color:#0000a0;font-weight:bold;">void</span><span
      style="color:#000000;"> </span><span
style="text-decoration:underline;text-decoration-style:solid;text-decoration-color:#0066cc;color:#0066cc;">addSlowEventWarning</span><span
      style="color:#000000;">(Scene scene) {</span></p><p
      style="margin:0;"><span style="color:#000000;">      </span><span
      style="color:#0000a0;font-weight:bold;">final</span><span
      style="color:#000000;"> EventDispatcher eventDispatcher = scene.getEventDispatcher();</span></p><p
      style="margin:0;"><span style="color:#000000;">  </span></p><p
      style="margin:0;"><span style="color:#000000;">      scene.setEventDispatcher(</span><span
      style="color:#0000a0;font-weight:bold;">new</span><span
      style="color:#000000;"> EventDispatcher() {</span></p><p
      style="margin:0;"><span style="color:#000000;">        </span><span
      style="color:#0000a0;font-weight:bold;">private</span><span
      style="color:#000000;"> ScheduledFuture<StackTraceElement[]> </span><span
      style="color:#0000c0;">future</span><span style="color:#000000;">;</span></p><p
      style="margin:0;"><span style="color:#000000;">  </span></p><p
      style="margin:0;"><span style="color:#000000;">        </span><span
      style="color:#646464;">@Override</span></p><p style="margin:0;"><span
      style="color:#000000;">        </span><span
      style="color:#0000a0;font-weight:bold;">public</span><span
      style="color:#000000;"> Event dispatchEvent(Event event, EventDispatchChain tail) {</span></p><p
      style="margin:0;"><span style="color:#000000;">          </span><span
      style="color:#0000a0;font-weight:bold;">if</span><span
      style="color:#000000;">(</span><span style="color:#0000c0;">future</span><span
      style="color:#000000;"> != </span><span
      style="color:#0000a0;font-weight:bold;">null</span><span
      style="color:#000000;">) {</span></p><p style="margin:0;"><span
      style="color:#000000;">            </span><span
      style="color:#0000c0;">future</span><span style="color:#000000;">.cancel(</span><span
      style="color:#0000a0;font-weight:bold;">false</span><span
      style="color:#000000;">);</span></p><p style="margin:0;"><span
      style="color:#000000;">          }</span></p><p style="margin:0;"><span
      style="color:#000000;">  </span></p><p style="margin:0;"><span
      style="color:#000000;">          </span><span
      style="color:#0000a0;font-weight:bold;">long</span><span
      style="color:#000000;"> startTime = System.</span><span
      style="color:#000000;font-style:italic;">currentTimeMillis</span><span
      style="color:#000000;">();</span></p><p style="margin:0;"><span
      style="color:#000000;">  </span></p><p style="margin:0;"><span
      style="color:#000000;">          </span><span
      style="color:#0000c0;">TIME_TRACKER</span><span
      style="color:#000000;">.enterNested(startTime);  </span><span
      style="color:#3f7f5f;">// nesting can happen in two ways, an event triggering another event, or when a nested event loop is entered</span></p><p
      style="margin:0;"><span style="color:#000000;">  </span></p><p
      style="margin:0;"><span style="color:#000000;">          Event returnedEvent = eventDispatcher.dispatchEvent(event, tail);</span></p><p
      style="margin:0;"><span style="color:#000000;">  </span></p><p
      style="margin:0;"><span style="color:#000000;">          </span><span
      style="color:#0000a0;font-weight:bold;">long</span><span
      style="color:#000000;"> endTime = System.</span><span
      style="color:#000000;font-style:italic;">currentTimeMillis</span><span
      style="color:#000000;">();</span></p><p style="margin:0;"><span
      style="color:#000000;">          </span><span
      style="color:#0000a0;font-weight:bold;">long</span><span
      style="color:#000000;"> timeSpentInNested = </span><span
      style="color:#0000c0;">TIME_TRACKER</span><span
      style="color:#000000;">.exitNested(endTime);</span></p><p
      style="margin:0;"><span style="color:#000000;">  </span></p><p
      style="margin:0;"><span style="color:#000000;">          </span><span
      style="color:#0000a0;font-weight:bold;">if</span><span
      style="color:#000000;">(timeSpentInNested > 10) {</span></p><p
      style="margin:0;"><span style="color:#000000;">            </span><span
      style="color:#0000a0;font-weight:bold;">long</span><span
      style="color:#000000;"> total = endTime - startTime;</span></p><p
      style="margin:0;"><span style="color:#000000;">  </span></p><p
      style="margin:0;"><span style="color:#000000;">            </span><span
      style="color:#0000c0;">LOGGER</span><span style="color:#000000;">.warning(</span><span
      style="color:#2a00ff;">"Slow Event (self/total: "</span><span
      style="color:#000000;"> + timeSpentInNested + </span><span
      style="color:#2a00ff;">"/"</span><span style="color:#000000;"> + total + </span><span
      style="color:#2a00ff;">" ms @ level "</span><span
      style="color:#000000;"> + </span><span style="color:#0000c0;">TIME_TRACKER</span><span
      style="color:#000000;">.getCurrentLevel() + </span><span
      style="color:#2a00ff;">"): "</span><span style="color:#000000;"> + event);</span></p><p
      style="margin:0;"><span style="color:#000000;">          }</span></p><p
      style="margin:0;"><span style="color:#000000;">  </span></p><p
      style="margin:0;"><span style="color:#000000;">          </span><span
      style="color:#7f0055;font-weight:bold;">return</span><span
      style="color:#000000;"> returnedEvent;</span></p><p
      style="margin:0;"><span style="color:#000000;">        }</span></p><p
      style="margin:0;"><span style="color:#000000;">      });</span></p><p
      style="margin:0;"><span style="color:#000000;">    }</span></p><p
      style="margin:0;"><span style="color:#000000;">
</span></p><p style="margin:0;"><span style="color:#000000;">--John
</span></p></div>
    </div>
    <p></p>
    <div class="moz-cite-prefix">On 05/08/2024 17:17, Thiago Milczarek
      Sayão wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAAP_wumFGfLeXc3yyt9NcVrAvbSH-MaJdK2rauYuHoiYtTFAiw@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div dir="ltr">
        <div dir="ltr">Hi,
          <div><br>
          </div>
          <div>Interesting idea. We have this problem specially when
            Junior developers touch the code.</div>
          <div><br>
          </div>
          <div>The other way around would be nice too - if some I/O task
            executes on the FX thread.</div>
          <div><br>
          </div>
          <div>This can make the OS think the application hanged and
            offer to kill it, since it won't respond to "pings". And I/O
            tasks processing time may vary between installations. Also
            causes "white screens" since it blocks painting.</div>
          <div><br>
          </div>
          <div>-- Thiago.</div>
        </div>
        <br>
        <div class="gmail_quote">
          <div dir="ltr" class="gmail_attr">Em seg., 5 de ago. de 2024
            às 11:59, Kevin Rushforth <<a
              href="mailto:kevin.rushforth@oracle.com"
              moz-do-not-send="true" class="moz-txt-link-freetext">kevin.rushforth@oracle.com</a>>
            escreveu:<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>
              <blockquote type="cite">Wouldn't it be better to implement
                this check in assert to avoid any impact in production?</blockquote>
              <br>
              No. Using an assert in a case like this is an
              anti-pattern. A call to assert in a library such as JavaFX
              is only appropriate for checking an invariant in internal
              logic. If we are going to go down this route of doing a
              thread check when mutating properties of "live" nodes, we
              will throw the same IllegalStateException that is
              currently thrown by some methods on Stage and Scene.<br>
              <br>
              As for the proposal itself, adding this check is an
              interesting idea. We considered doing this back in the JDK
              7 (JavaFX 2) time frame, but decided not to pursue it
              then. I think the idea is worth further discussion. I
              would limit any thread checking to setting the property.
              It would be too restrictive (and largely unnecessary) to
              prevent reading a property from the application thread.<br>
              <br>
              The things to consider would be:<br>
              <br>
              1. What is the performance hit of doing this check on the
              setting of every property?<br>
              2. What is the effect on bound properties?<br>
              3. How intrusive is it in the code?<br>
              4. Should we add a property to enable / disable the thread
              check, possibly a three- or four-valued property
              (allow|warn|debug?|deny), as was recently done in JEP 471
              for sun.misc.Unsafe memory access methods. If so, what
              should the default be?<br>
              <br>
              My quick take is that if this can be done in a minimally
              intrusive manner with low overhead, we should consider
              pursing this. As for 4, my preference would be to add a
              three- or four-valued system property to control the
              check, with "warn" as the default initially, changing the
              default to "disallow" in a subsequent version. This would,
              of course, require a lot of testing.<br>
              <br>
              -- Kevin<br>
              <br>
              <br>
              <div>On 8/4/2024 8:40 PM, quizynox wrote:<br>
              </div>
              <blockquote type="cite">
                <div dir="ltr">Hello,<br>
                  <br>
                  Wouldn't it be better to implement this check in
                  assert to avoid any impact in production?</div>
                <br>
                <div class="gmail_quote">
                  <div dir="ltr" class="gmail_attr">пн, 5 авг. 2024 г. в
                    03:30, John Hendrikx <<a
                      href="mailto:john.hendrikx@gmail.com"
                      target="_blank" moz-do-not-send="true"
                      class="moz-txt-link-freetext">john.hendrikx@gmail.com</a>>:<br>
                  </div>
                  <blockquote class="gmail_quote"
style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi
                    list,<br>
                    <br>
                    I know of quite some bugs and users that have been
                    bitten by the <br>
                    threading model used by JavaFX.  Basically, anything
                    directly or <br>
                    indirectly linked to an active Scene must be
                    accessed on the FX thread.  <br>
                    However, as FX also allows manipulating nodes and
                    properties before <br>
                    they're displayed, there can be no "hard" check
                    everywhere to ensure we <br>
                    are on the FX thread (specifically, in properties).<br>
                    <br>
                    Now, I think this situation is annoying, as a simple
                    mistake where a <br>
                    Platform.runLater wrapper was forgotten usually
                    results in programs <br>
                    operating mostly flawlessly, but then fail in
                    mysterious and random and <br>
                    hard to reproduce ways.  The blame is often put on
                    FX as the resulting <br>
                    exceptions will almost never show the user code
                    which was the actual <br>
                    culprit.  It can result in FX being perceived as
                    unstable or buggy.<br>
                    <br>
                    So I've been thinking if there isn't something we
                    can do to detect these <br>
                    bugs originating from user code much earlier,
                    similar to the <br>
                    `ConcurrentModificationException` the collection
                    classes do when <br>
                    accessed in nested or concurrent contexts.<br>
                    <br>
                    I think it may be possible to have properties check
                    whether they're part <br>
                    of an active scene without too much of an
                    performance impact, possibly <br>
                    even behind a switch. It would work like this:<br>
                    <br>
                    Properties involved with Nodes will have an
                    associated bean instance <br>
                    (`getBean`).  This is an object, but we could check
                    here if this <br>
                    instance implements an interface:<br>
                    <br>
                          if (getBean() instanceof MayBePartOfSceneGraph
                    x) {<br>
                                if (x.isPartOfActiveScene() &&
                    !isOnFxThread()) {<br>
                                     throw new
                    IllegalStateException("Property must only be <br>
                    used from the FX Application Thread");<br>
                                }<br>
                          }<br>
                    <br>
                    This check could be done on every set of the
                    property, and potentially <br>
                    on every get as well.  It should be relatively
                    cheap, but will expose <br>
                    problematic code patterns at a much earlier stage. 
                    There's a chance <br>
                    that this will "break" some programs that seemed to
                    be behaving <br>
                    correctly as well, so we may want to put it behind a
                    switch until such <br>
                    programs (or libraries) can be fixed.<br>
                    <br>
                    What do you all think?<br>
                    <br>
                    --John<br>
                    <br>
                    (*) Names of methods/interfaces are only used for
                    illustration purposes, <br>
                    we can think of good names if this moves forward.<br>
                    <br>
                  </blockquote>
                </div>
              </blockquote>
              <br>
            </div>
          </blockquote>
        </div>
      </div>
    </blockquote>
  </body>
</html>