Skip to content

Commit

Permalink
8159048: Animation and AnimationTimer methods must be called on JavaF…
Browse files Browse the repository at this point in the history
…X Application thread

Reviewed-by: angorya, kcr
  • Loading branch information
Jose Pereda committed Aug 14, 2023
1 parent a5183e5 commit c3257fc
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -83,7 +83,8 @@
* the {@code Animation} from the specified position.
* <p>
* Inverting the value of {@link #rateProperty() rate} toggles the play direction.
*
* <p>
* The animation runs on the JavaFX Application Thread.
* @see Timeline
* @see Transition
*
Expand Down Expand Up @@ -978,12 +979,15 @@ public void playFromStart() {
* Note: <ul>
* <li>{@code play()} is an asynchronous call, the {@code Animation} may not
* start immediately. </ul>
* <p>
* This method must be called on the JavaFX Application thread.
*
* @throws IllegalStateException
* if embedded in another animation,
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread, or if embedded in another animation,
* such as {@link SequentialTransition} or {@link ParallelTransition}
*/
public void play() {
Toolkit.getToolkit().checkFxUserThread();
if (parent != null) {
throw new IllegalStateException("Cannot start when embedded in another animation");
}
Expand Down Expand Up @@ -1035,11 +1039,15 @@ void doResume() {
* Note: <ul>
* <li>{@code stop()} is an asynchronous call, the {@code Animation} may not stop
* immediately. </ul>
* @throws IllegalStateException
* if embedded in another animation,
* <p>
* This method must be called on the JavaFX Application thread.
*
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread, or if embedded in another animation,
* such as {@link SequentialTransition} or {@link ParallelTransition}
*/
public void stop() {
Toolkit.getToolkit().checkFxUserThread();
if (parent != null) {
throw new IllegalStateException("Cannot stop when embedded in another animation");
}
Expand All @@ -1066,11 +1074,15 @@ void doStop() {
* Note: <ul>
* <li>{@code pause()} is an asynchronous call, the {@code Animation} may not pause
* immediately. </ul>
* @throws IllegalStateException
* if embedded in another animation,
* <p>
* This method must be called on the JavaFX Application thread.
*
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread, or if embedded in another animation,
* such as {@link SequentialTransition} or {@link ParallelTransition}
*/
public void pause() {
Toolkit.getToolkit().checkFxUserThread();
if (parent != null) {
throw new IllegalStateException("Cannot pause when embedded in another animation");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -35,13 +35,14 @@
/**
* The class {@code AnimationTimer} allows to create a timer, that is called in
* each frame while it is active.
*
* <p>
* An extending class has to override the method {@link #handle(long)} which
* will be called in every frame.
*
* <p>
* The methods {@link AnimationTimer#start()} and {@link #stop()} allow to start
* and stop the timer.
*
* <p>
* The animation timer runs on the JavaFX Application Thread.
*
* @since JavaFX 2.0
*/
Expand Down Expand Up @@ -96,11 +97,17 @@ public AnimationTimer() {
* Starts the {@code AnimationTimer}. Once it is started, the
* {@link #handle(long)} method of this {@code AnimationTimer} will be
* called in every frame.
*
* <p>
* The {@code AnimationTimer} can be stopped by calling {@link #stop()}.
* <p>
* This method must be called on the JavaFX Application thread.
*
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread.
*/
@SuppressWarnings("removal")
public void start() {
Toolkit.getToolkit().checkFxUserThread();
if (!active) {
// Capture the Access Control Context to be used during the animation pulse
accessCtrlCtx = AccessController.getContext();
Expand All @@ -112,8 +119,14 @@ public void start() {
/**
* Stops the {@code AnimationTimer}. It can be activated again by calling
* {@link #start()}.
* <p>
* This method must be called on the JavaFX Application thread.
*
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread.
*/
public void stop() {
Toolkit.getToolkit().checkFxUserThread();
if (active) {
timer.removeAnimationTimer(timerReceiver);
active = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.com.sun.javafx.animation;

import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import test.util.Util;

import java.util.concurrent.CountDownLatch;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class AnimationTest {

private static final CountDownLatch startupLatch = new CountDownLatch(1);

private static Stage primaryStage;

public static class TestApp extends Application {

@Override
public void init() throws Exception {
assertFalse(Platform.isFxApplicationThread());
}

@Override
public void start(Stage stage) throws Exception {
primaryStage = stage;
assertTrue(Platform.isFxApplicationThread());

startupLatch.countDown();
}

}

@BeforeClass
public static void setup() throws Exception {
Util.launch(startupLatch, TestApp.class);
}

@AfterClass
public static void shutdown() {
Util.shutdown(primaryStage);
}

@Test
public void animationOnFXThreadTest() throws InterruptedException {
final CountDownLatch l = new CountDownLatch(1);
Platform.runLater(() -> {
assertTrue(Platform.isFxApplicationThread());
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.play();
pause.pause();
pause.stop();
l.countDown();
});
l.await();
}

@Test
public void startAnimationNotOnFXThreadTest() {
assertFalse(Platform.isFxApplicationThread());
PauseTransition pause = new PauseTransition(Duration.seconds(1));
assertThrows(IllegalStateException.class, pause::play);
}

@Test
public void pauseAnimationNotOnFXThreadTest() {
assertFalse(Platform.isFxApplicationThread());
PauseTransition pause = new PauseTransition(Duration.seconds(1));
Platform.runLater(pause::play);
assertThrows(IllegalStateException.class, pause::pause);
}

@Test
public void stopAnimationNotOnFXThreadTest() {
assertFalse(Platform.isFxApplicationThread());
PauseTransition pause = new PauseTransition(Duration.seconds(1));
Platform.runLater(() -> {
pause.play();
pause.pause();
});
assertThrows(IllegalStateException.class, pause::stop);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.com.sun.javafx.animation;

import java.util.concurrent.CountDownLatch;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import test.util.Util;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class AnimationTimerTest {

private static final CountDownLatch startupLatch = new CountDownLatch(1);

private static Stage primaryStage;

public static class TestApp extends Application {

@Override
public void init() throws Exception {
assertFalse(Platform.isFxApplicationThread());
}

@Override
public void start(Stage stage) throws Exception {
primaryStage = stage;
assertTrue(Platform.isFxApplicationThread());

startupLatch.countDown();
}

}

@BeforeClass
public static void setup() throws Exception {
Util.launch(startupLatch, TestApp.class);
}

@AfterClass
public static void shutdown() {
Util.shutdown(primaryStage);
}

@Test
public void animationTimerOnFXThreadTest() throws InterruptedException {
final CountDownLatch frameCounter = new CountDownLatch(3);
Platform.runLater(() -> {
AnimationTimer timer = new AnimationTimer() {
@Override public void handle(long l) {
frameCounter.countDown();
if (frameCounter.getCount() == 0L) {
stop();
}
}
};
assertTrue(Platform.isFxApplicationThread());
timer.start();
});
frameCounter.await();
}

@Test
public void startAnimationTimerNotOnFXThreadTest() {
assertFalse(Platform.isFxApplicationThread());
AnimationTimer timer = new AnimationTimer() {
@Override public void handle(long l) {}
};
assertThrows(IllegalStateException.class, timer::start);
}

@Test
public void stopAnimationTimerNotOnFXThreadTest() {
assertFalse(Platform.isFxApplicationThread());
AnimationTimer timer = new AnimationTimer() {
@Override public void handle(long l) {
assertThrows(IllegalStateException.class, () -> stop());
}
};
Platform.runLater(timer::start);
}
}

1 comment on commit c3257fc

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.