JDK-8058870 : Mac: JFXPanel deadlocks in jnlp mode
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 8u40
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x
  • Submitted: 2014-09-22
  • Updated: 2015-06-04
  • Resolved: 2014-09-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u40Fixed 9 b36Fixed
Description
The following app hangs when launched on MacOS X with jdk8u40 as a jnlp app (netbeans project is attached):

package cft;

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class CFT {
         private static void initAndShowGUI() {
             JFrame frame = new JFrame("FX");
             frame.setSize(100, 100);
             final JFXPanel fxPanel = new JFXPanel();
             frame.add(fxPanel);
             frame.setVisible(true);

             Platform.runLater(new Runnable() {
                 @Override
                 public void run() {
                     initFX(fxPanel);
                 }
             });
         }

         private static void initFX(JFXPanel fxPanel) {
             Scene scene = createScene();
             fxPanel.setScene(scene);
         }

         private static Scene createScene() {
             StackPane p = new StackPane();
 	     Scene scene = new Scene(p);
             p.getChildren().add(new Button("fx button"));
             return scene;
	 }

         public static void main(String[] args) {
             SwingUtilities.invokeLater(new Runnable() {
                 @Override
                 public void run() {
                     initAndShowGUI();
                 }
             });
         }
}

Comments
last webrev: http://cr.openjdk.java.net/~ant/JDK-8058870/webrev.1 review thread: http://mail.openjdk.java.net/pipermail/swing-dev/2014-September/003943.html
24-09-2014

webrev: http://cr.openjdk.java.net/~ant/JDK-8058870/webrev.0 The fix eliminates the JComponent.this usage as a lock in the 3rd case listed above. (I have to note though, that nevertheless the fix resolves this particular deadlock case, there's still a potential threat that the same deadlock scenario can be reproduced otherwise.) Namely, the fix removes the "synchronised" block at all, relying on the fact that a read/write of a primitive int is an atomic op. For that, the get/set methods are slightly modified so that a read/write to the "flags" field would be a single op. This lock-free sync scheme should keep the original logic unchanged for this particular case.
22-09-2014

Holding on a Component.this lock is dangerous and shouldn't be done without a strong need. Here, the 3rd code spot looks like a weak link: JComponent.revalidate: // To avoid a flood of Runnables when constructing GUIs off // the EDT, a flag is maintained as to whether or not // a Runnable has been scheduled. synchronized(this) { if (getFlag(REVALIDATE_RUNNABLE_SCHEDULED)) { return; } setFlag(REVALIDATE_RUNNABLE_SCHEDULED, true); } SunToolkit.executeOnEventHandlerThread(this, () -> { synchronized(JComponent.this) { setFlag(REVALIDATE_RUNNABLE_SCHEDULED, false); } revalidate(); }); Here, JComponent.this obj is used just to synchronise access to the "flags" instance field. The only listed code is involved, that is, this lock scheme doesn't assume any other code involvement, for which using JComponent.this as a lock could be justified. So, here we can easily avoid locking on JComponent.this.
22-09-2014

This deadlock is as follows: "AWT-EventQueue-2" #27 prio=6 os_prio=31 tid=0x00007f9bc32dc800 nid=0xb003 runnable [0x00000001e521e000] java.lang.Thread.State: RUNNABLE at sun.lwawt.macosx.CCursorManager.nativeGetCursorPosition(Native Method) at sun.lwawt.macosx.CCursorManager.getCursorPosition(CCursorManager.java:54) at sun.lwawt.LWCursorManager.updateCursorImpl(LWCursorManager.java:80) at sun.lwawt.LWCursorManager.updateCursor(LWCursorManager.java:57) at sun.lwawt.LWComponentPeer.updateCursorImmediately(LWComponentPeer.java:896) at java.awt.Component.updateCursorImmediately(Component.java:3140) at java.awt.Container.validate(Container.java:1640) at java.awt.Window.dispatchEventImpl(Window.java:2737) at java.awt.Component.dispatchEvent(Component.java:4703) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:751) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:702) at java.awt.EventQueue$3.run(EventQueue.java:696) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86) at java.awt.EventQueue$4.run(EventQueue.java:724) at java.awt.EventQueue$4.run(EventQueue.java:722) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75) at java.awt.EventQueue.dispatchEvent(EventQueue.java:721) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) "AWT-EventQueue-0" #12 prio=6 os_prio=31 tid=0x00007f9bc3156800 nid=0x9c03 runnable [0x00000001e3e8f000] java.lang.Thread.State: RUNNABLE at sun.lwawt.macosx.CDropTarget.createNativeDropTarget(Native Method) at sun.lwawt.macosx.CDropTarget.<init>(CDropTarget.java:58) at sun.lwawt.macosx.CDropTarget.createDropTarget(CDropTarget.java:44) at sun.lwawt.LWComponentPeer.addDropTarget(LWComponentPeer.java:1072) - locked <0x0000000170802a78> (a java.lang.Object) at java.awt.dnd.DropTarget.addNotify(DropTarget.java:514) at java.awt.Component.setDropTarget(Component.java:1107) - locked <0x00000001708020c8> (a javafx.embed.swing.JFXPanel) at java.awt.dnd.DropTarget.<init>(DropTarget.java:109) at java.awt.dnd.DropTarget.<init>(DropTarget.java:141) at java.awt.dnd.DropTarget.<init>(DropTarget.java:189) at javafx.embed.swing.SwingDnD.<init>(SwingDnD.java:218) at javafx.embed.swing.JFXPanel$HostContainer.lambda$setEmbeddedScene$53(JFXPanel.java:855) at javafx.embed.swing.JFXPanel$HostContainer$$Lambda$83/2134489636.run(Unknown Source) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:749) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:702) at java.awt.EventQueue$3.run(EventQueue.java:696) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75) at java.awt.EventQueue.dispatchEvent(EventQueue.java:719) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) "JavaFX Application Thread" #8 daemon prio=5 os_prio=31 tid=0x00007f9bc30ea800 nid=0x707 waiting for monitor entry [0x00007fff572a8000] java.lang.Thread.State: BLOCKED (on object monitor) at javax.swing.JComponent.revalidate(JComponent.java:4863) - waiting to lock <0x00000001708020c8> (a javafx.embed.swing.JFXPanel) at javafx.embed.swing.JFXPanel$HostContainer.setPreferredSize(JFXPanel.java:883) at com.sun.javafx.tk.quantum.EmbeddedStage.setBounds(EmbeddedStage.java:77) at javafx.stage.Window$TKBoundsConfigurator.apply(Window.java:1254) at javafx.stage.Window.applyBounds(Window.java:1144) at javafx.stage.Window.adjustSize(Window.java:241) at javafx.stage.Window.access$600(Window.java:77) at javafx.stage.Window$9.invalidated(Window.java:831) at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:109) at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:144) at javafx.stage.Window.setShowing(Window.java:902) at javafx.stage.Window.show(Window.java:917) at com.sun.javafx.stage.EmbeddedWindow.show(EmbeddedWindow.java:58) at javafx.embed.swing.JFXPanel.setSceneImpl(JFXPanel.java:284) at javafx.embed.swing.JFXPanel.setScene(JFXPanel.java:254) at cft.CFT.initFX(CFT.java:34) at cft.CFT.access$000(CFT.java:16) at cft.CFT$1.run(CFT.java:27) at com.sun.javafx.application.PlatformImpl.lambda$null$165(PlatformImpl.java:295) at com.sun.javafx.application.PlatformImpl$$Lambda$57/1116864388.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.application.PlatformImpl.lambda$runLater$166(PlatformImpl.java:294) at com.sun.javafx.application.PlatformImpl$$Lambda$56/1223058699.run(Unknown Source) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) 1. "AWT-EventQueue-2" #27 hangs in CCursorManager.nativeGetCursorPosition waiting for sync execution on Main thread (which is "JavaFX App Thread" as well). 2. "AWT-EventQueue-0" #12 hangs in CDropTarget.createNativeDropTarget by the same reason, holding JFXPanel.this lock. 3. "JavaFX Application Thread" hangs in JComponent.revalidate, waiting to lock JFXPanel.this, held by "AWT-EventQueue-0" #12.
22-09-2014