On Fri, 16 May 2025 14:38:20 GMT, Michael Strauß <mstra...@openjdk.org> wrote:

> When an exception is thrown from `AnimationTimer::handle`, the JavaFX 
> application freezes. The reason is that the user exception will bubble up 
> into framework code, preventing the normal operation of JavaFX.
> 
> The following program demonstrates the defect:
> 
> 
> public class FailingAnimationTimer extends Application {
>     @Override
>     public void start(Stage stage) throws Exception {
>         var button = new Button("start timer");
>         button.setOnAction(_ -> {
>             var timer = new AnimationTimer() {
>                 @Override
>                 public void handle(long l) {
>                     throw new RuntimeException("foo");
>                 }
>             };
> 
>             timer.start();
>         });
> 
>         var root = new HBox();
>         root.getChildren().add(new TextField("test"));
>         root.getChildren().add(button);
>         stage.setScene(new Scene(root));
>         stage.show();
>     }
> }
> 
> 
> The solution is to not allow user exceptions to bubble up into animation 
> framework code. If an exception occurs, it is instead sent to the current 
> thread's uncaught exception handler. This is the same thing that we already 
> do for exceptions thrown by invalidation listeners and change listeners.
> 
> In addition to that, a failing animation timer has the potential to spam 
> logs, which is why I introduced a cut-off value at 100 exceptions for each 
> individual timer, after which no further exceptions from this particular 
> timer are sent to the uncaught exception handler. After reaching the cut-off 
> value, the following warning is logged:
> 
> `WARNING: Too many exceptions thrown by AnimationTimer, ignoring further 
> exceptions. The cut-off number can be set with the system property 
> com.sun.scenario.animation.failingTimerThreshold (current = 100).`

modules/javafx.graphics/src/main/java/com/sun/scenario/animation/AbstractPrimaryTimer.java
 line 66:

> 64:     private final int PULSE_DURATION_TICKS = 
> getPulseDuration((int)TickCalculation.fromMillis(1000));
> 65: 
> 66:     private static final String FAILING_TIMER_THRESHOLD_PROP = 
> "com.sun.scenario.animation.failingTimerThreshold";

maybe a comment describing what this property does would be helpful for the 
digital paleontologist of the future

modules/javafx.graphics/src/main/java/com/sun/scenario/animation/AbstractPrimaryTimer.java
 line 331:

> 329:         }
> 330: 
> 331:         receiversLocked = false;

if L327 throws an exception, `receiversLocked` will not get cleared.

modules/javafx.graphics/src/main/java/com/sun/scenario/animation/AbstractPrimaryTimer.java
 line 347:

> 345:         }
> 346: 
> 347:         animationTimersLocked = false;

same here

-------------

PR Review Comment: https://git.openjdk.org/jfx/pull/1811#discussion_r2093467739
PR Review Comment: https://git.openjdk.org/jfx/pull/1811#discussion_r2093466067
PR Review Comment: https://git.openjdk.org/jfx/pull/1811#discussion_r2093466315

Reply via email to