From ed19741dc064fb107e299da304b55d4500f7ae54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20R=C5=AF=C5=BEi=C4=8Dka?= Date: Thu, 19 Sep 2019 23:58:53 +0200 Subject: [PATCH 1/3] 8231372: Correctly terminate secondary event loop in JFXPanel.setScene() Secondary event loop introduced as a means of synchronization with the JavaFX Application thread in [1] never terminates as the SecondaryLoop.exit() call is not reached because the thread is blocked in the SecondaryLoop.enter() call. This patch fixes the problem by submitting the UI work (including the call to the SecondaryLoop.exit() method) before entering the secondary loop. [1] https://github.com/openjdk/jfx/commit/7cf2dfa0b3c5bfd0f1a2de36d46b62f7e9e256c4 --- .../src/main/java/javafx/embed/swing/JFXPanel.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index edfe0464ce4..516af0f353f 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -299,12 +299,11 @@ public void setScene(final Scene newScene) { (PrivilegedAction) java.awt.Toolkit .getDefaultToolkit()::getSystemEventQueue); SecondaryLoop secondaryLoop = eventQueue.createSecondaryLoop(); - if (secondaryLoop.enter()) { - Platform.runLater(() -> { - setSceneImpl(newScene); - }); + Platform.runLater(() -> { + setSceneImpl(newScene); secondaryLoop.exit(); - } + }); + secondaryLoop.enter(); } } From 6039460b8c30455b32add28345962f961be649c3 Mon Sep 17 00:00:00 2001 From: Kevin Rushforth Date: Thu, 3 Dec 2020 17:32:16 -0800 Subject: [PATCH 2/3] Add automated tests, enclose the latch in try/finally, --- .../java/javafx/embed/swing/JFXPanel.java | 7 ++- .../test/javafx/embed/swing/JFXPanelTest.java | 55 ++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index 516af0f353f..21f6ae430ec 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -300,8 +300,11 @@ public void setScene(final Scene newScene) { .getDefaultToolkit()::getSystemEventQueue); SecondaryLoop secondaryLoop = eventQueue.createSecondaryLoop(); Platform.runLater(() -> { - setSceneImpl(newScene); - secondaryLoop.exit(); + try { + setSceneImpl(newScene); + } finally { + secondaryLoop.exit(); + } }); secondaryLoop.enter(); } diff --git a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java index 217295207a1..0054be541f0 100644 --- a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java +++ b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java @@ -155,5 +155,58 @@ public void testClickOnEmptyJFXPanel() throws Exception { Assert.assertTrue(firstPressedEventLatch.await(5000, TimeUnit.MILLISECONDS)); } -} + @Test + public void setSceneOnFXThread() throws Exception { + + CountDownLatch completionLatch = new CountDownLatch(1); + + SwingUtilities.invokeLater(() -> { + JFXPanel fxPanel = new JFXPanel(); + fxPanel.setPreferredSize(new Dimension(100, 100)); + JFrame jframe = new JFrame(); + JPanel jpanel = new JPanel(); + jpanel.add(fxPanel); + jframe.add(jpanel); + jframe.pack(); + jframe.setVisible(true); + + Platform.runLater(() -> { + Scene scene = new Scene(new Group()); + fxPanel.setScene(scene); + completionLatch.countDown(); + }); + }); + + Assert.assertTrue("Timeout waiting for setScene to complete", + completionLatch.await(5000, TimeUnit.MILLISECONDS)); + } + + @Test + public void setSceneOnSwingThread() throws Exception { + + CountDownLatch completionLatch = new CountDownLatch(1); + + SwingUtilities.invokeLater(() -> { + JFXPanel fxPanel = new JFXPanel(); + fxPanel.setPreferredSize(new Dimension(100, 100)); + JFrame jframe = new JFrame(); + JPanel jpanel = new JPanel(); + jpanel.add(fxPanel); + jframe.add(jpanel); + jframe.pack(); + jframe.setVisible(true); + + Platform.runLater(() -> { + Scene scene = new Scene(new Group()); + SwingUtilities.invokeLater(() -> { + fxPanel.setScene(scene); + completionLatch.countDown(); + }); + }); + }); + + Assert.assertTrue("Timeout waiting for setScene to complete", + completionLatch.await(5000, TimeUnit.MILLISECONDS)); + } +} From 22e3886d13c2ac548669ba858ebaea409b6e535a Mon Sep 17 00:00:00 2001 From: Kevin Rushforth Date: Sat, 5 Dec 2020 05:35:13 -0800 Subject: [PATCH 3/3] Dispose JFrame after each test --- .../test/javafx/embed/swing/JFXPanelTest.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java index 0054be541f0..95efad1bfd9 100644 --- a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java +++ b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelTest.java @@ -28,6 +28,7 @@ import org.junit.Assume; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.After; import org.junit.AfterClass; import org.junit.Test; import junit.framework.AssertionFailedError; @@ -54,6 +55,8 @@ public class JFXPanelTest { // Used to launch the application before running any test private static final CountDownLatch launchLatch = new CountDownLatch(1); + JFrame jframe; + // Application class. An instance is created and initialized before running // the first test, and it lives through the execution of all tests. public static class MyApp extends Application { @@ -84,6 +87,13 @@ public static void doTeardownOnce() { Platform.exit(); } + @After + public void doCleanup() { + if (jframe != null) { + SwingUtilities.invokeLater(() -> jframe.dispose()); + } + } + static class TestFXPanel extends JFXPanel { protected void processMouseEventPublic(MouseEvent e) { processMouseEvent(e); @@ -103,7 +113,7 @@ public void testNoDoubleClickOnFirstClick() throws Exception { dummyFXPanel.setPreferredSize(new Dimension(100, 100)); TestFXPanel fxPnl = new TestFXPanel(); fxPnl.setPreferredSize(new Dimension(100, 100)); - JFrame jframe = new JFrame(); + jframe = new JFrame(); JPanel jpanel = new JPanel(); jpanel.add(dummyFXPanel); jpanel.add(fxPnl); @@ -164,7 +174,7 @@ public void setSceneOnFXThread() throws Exception { SwingUtilities.invokeLater(() -> { JFXPanel fxPanel = new JFXPanel(); fxPanel.setPreferredSize(new Dimension(100, 100)); - JFrame jframe = new JFrame(); + jframe = new JFrame(); JPanel jpanel = new JPanel(); jpanel.add(fxPanel); jframe.add(jpanel); @@ -190,7 +200,7 @@ public void setSceneOnSwingThread() throws Exception { SwingUtilities.invokeLater(() -> { JFXPanel fxPanel = new JFXPanel(); fxPanel.setPreferredSize(new Dimension(100, 100)); - JFrame jframe = new JFrame(); + jframe = new JFrame(); JPanel jpanel = new JPanel(); jpanel.add(fxPanel); jframe.add(jpanel);