<!DOCTYPE html><html><head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  
<style type="text/css">body { font-family:'Calibri'; font-size:13px}</style>
</head>
<body><div><big>Hi John and others,</big></div><div><big><br></big></div><div><big>I don't think we are entirely on the same page, so here's the objective.<br><br>The Goal: To determine if the FX animation thread and a SINGLE other thread can access Animation in a safe manner wrt play, stop,  and resume.<br><br>Non Goal: Multi-threaded access of Animation play, stop, and resume is NOT a goal.<br><br>Wrt play() and resume(), it is ALWAYS safe to call them on a background thread because at this point the Animation isn't being processed by  the FX thread as the "Animation" isn't contained in the AbstractPrimaryTimer receivers array.<br><br>Wrt stop() from what I can tell there are no shared method calls that need synchronization (see audit below), however the following two boolean flags  should be marked as volatile in order to ensure that animation is cut short if executing:<br><br>TimelineClipCore.abort<br>ClipEnvelope.abort<br><br>This is simple enough to add to my original proposal of replacing the arrays in AbstractPrimaryTimer with CopyOnWriteArrayList which  is thread safe and replicates the intended behavior in a clear and concise way.<br><br></big><big>Here are </big>
<big>the methods that are called when</big> <big>stop() is invoked:<br></big></div><div><big><br></big></div><div><div style="background-color:#ffffff;padding:0px 2px 0px 2px;"><div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:10pt;white-space:pre;"><p style="margin:0;"><span style="color:#3f5fbf;">Timeline.stop()</span></p><p style="margin:0;"><span style="color:#3f5fbf;">   getStatus()</span></p><p style="margin:0;"><span style="color:#3f5fbf;">   clipCore.abort()</span></p><p style="margin:0;"><span style="color:#3f5fbf;">   isStopped()</span></p><p style="margin:0;"><span style="color:#3f5fbf;">   clipEnvelope.abortCurrentPulse()</span></p><p style="margin:0;"><span style="color:#3f5fbf;">   doStop()</span></p><p style="margin:0;"><span style="color:#3f5fbf;">      timer.removePulseReceiver(pulseReceiver);<br></span><div style="background-color:#ffffff;padding:0px 2px 0px 2px;"><div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:10pt;white-space:pre;"><p style="margin:0;"><span style="color:#3f5fbf;">      // After this point it doesn't matter</span></p></div></div><span style="color:#3f5fbf;">      setStatus(Status.STOPPED)</span></p><p style="margin:0;"><span style="color:#3f5fbf;">      doSetCurrentRate(0.0)</span></p><p style="margin:0;"><span style="color:#3f5fbf;">      jumpTo(Duration.ZERO)</span></p></div></div><big><br></big></div><div><big>And for AbstractPrimaryTimer.timePulseImpl:<br></big></div><div><big><br></big></div><div><div style="background-color:#ffffff;padding:0px 2px 0px 2px;"><div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:10pt;white-space:pre;"><p style="margin:0;"><span style="color:#3f7f5f;">AbstractPrimaryTimer.timePulseImpl</span></p><p style="margin:0;"><span style="color:#3f7f5f;">   Animation.PulseReceiver.timePulse</span></p><p style="margin:0;"><span style="color:#3f7f5f;">      Animation.doTimePulse</span></p><p style="margin:0;"><span style="color:#3f7f5f;">         clipEnvelope.timePulse</span></p><p style="margin:0;"><span style="color:#3f7f5f;">             animation.doPlayTo</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                clipCore.playTo</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                   visitKeyFrame</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                      setTime</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                         animation.setCurrentTicks</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                      clipInterpolator.interpolate</span></p><p style="margin:0;"><span style="color:#3f7f5f;">             animation.doJumpTo</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                sync(false)</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                setCurrentTicks</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                clipCore.jumpTo</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                   timeline.getStatus()</span></p><p style="margin:0;"><span style="color:#3f7f5f;">                   clipInterpolator.interpolate</span></p></div></div><big><br></big></div><div><big>Regards<br></big></div><div><big>Jurgen<br></big></div><div><big><br></big></div><div><br></div><div>On Tue, 23 Jan 2024 01:52:42 +0200, John Hendrikx <john.hendrikx@gmail.com> wrote:<br></div><blockquote style="margin: 0 0 0.80ex; border-left: #0000FF 2px solid; padding-left: 1ex">
    <p>This is not a good idea, the NPE is a symptom of the problem, but
      there can be many more subtler problems (and new ones may surface
      after fixing the NPE) that can affect other animations making use
      of the shared structures involved.  For one thing, absent a method
      of synchronization, Java doesn't even guarantee that fields (or
      arrays) contain the same value for all threads.  So your thread
      may modify some state, but the FX thread wouldn't even know about
      it (or only partially), and may make assumptions about an
      incorrect state, and then make further changes based on incorrect
      assumptions.<br>
    </p>
    <p>A good example is that even a much simpler class like HashMap
      when accessed by multiple threads can get into a really bad
      state.  It will usually throw a "ConcurrentModificationException"
      (or perhaps a weird NPE or IllegalStateException) to alert you
      that you're using it wrong -- but those are the **best** case
      scenarios... In the worst case scenario, modifying a HashMap on
      multiple threads can result in an infinite loop -- this can happen
      when two modifications occur, both updating the same bucket, and
      the threads end up changing things in such a way that the entries
      point to each other.  Iterating it then (or accessing the wrong
      key) will cause an infinite loop (not hypothetical, I've had this
      happen).<br>
    </p>
    <p>I'm afraid that even the synchronization option I proposed is not
      very realistic due to the amount of work it will entail.  We'd
      need to trace all code paths that a call to play or stop could get
      to, and all those code paths would need to be made thread safe.<br>
    </p>
    <p>--John</p></blockquote></body></html>