JDK-8221261 : Deadlock on macOS in JFXPanel app when handling IME calls
  • Type: Bug
  • Component: javafx
  • Sub-Component: window-toolkit
  • Affected Version: jfx11,8
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x
  • Submitted: 2019-03-21
  • Updated: 2024-02-22
  • Resolved: 2024-01-15
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 Other
8u411Fixed jfx17.0.11Fixed
Related Reports
Blocks :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Below is the stack trace showing a deadlock reproduced when using WebView via JFXPanel in JetBrains IDE:

"AWT-EventQueue-0" prio=0 tid=0x0 nid=0x0 waiting on condition
      java.lang.Thread.State: WAITING on java.util.concurrent.FutureTask at 51c6338d
     at java.base at 11.0.2/jdk.internal.misc.Unsafe.park(Native Method)
     at java.base at 11.0.2/java.util.concurrent.locks.LockSupport.park(LockSupport.java:194)
     at java.base at 11.0.2/java.util.concurrent.FutureTask.awaitDone(FutureTask.java:447)
     at java.base at 11.0.2/java.util.concurrent.FutureTask.get(FutureTask.java:190)
     at platform/javafx.web at 11.0.2/com.sun.javafx.webkit.InputMethodClientImpl.getLocationOffset(InputMethodClientImpl.java:157)
     at platform/javafx.graphics at 11.0.2/javafx.scene.Scene$InputMethodRequestsDelegate.getLocationOffset(Scene.java:4140)
     at platform/javafx.swing at 11.0.2/javafx.embed.swing.InputMethodSupport$InputMethodRequestsAdapter.getLocationOffset(InputMethodSupport.java:67)
     at java.desktop at 11.0.2/sun.awt.im.InputMethodContext.getLocationOffset(InputMethodContext.java:285)
     at java.desktop at 11.0.2/sun.lwawt.macosx.CInputMethod$7.run(CInputMethod.java:779)
     at java.desktop at 11.0.2/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:303)
     at java.desktop at 11.0.2/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:776)
     at java.desktop at 11.0.2/java.awt.EventQueue$4.run(EventQueue.java:727)
     at java.desktop at 11.0.2/java.awt.EventQueue$4.run(EventQueue.java:721)
     at java.base at 11.0.2/java.security.AccessController.doPrivileged(Native Method)
     at java.base at 11.0.2/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
     at java.base at 11.0.2/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
     at java.desktop at 11.0.2/java.awt.EventQueue$5.run(EventQueue.java:751)
     at java.desktop at 11.0.2/java.awt.EventQueue$5.run(EventQueue.java:749)
     at java.base at 11.0.2/java.security.AccessController.doPrivileged(Native Method)
     at java.base at 11.0.2/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
     at java.desktop at 11.0.2/java.awt.EventQueue.dispatchEvent(EventQueue.java:748)
     at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:723)
     at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:672)
     at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:367)
     at java.desktop at 11.0.2/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
     at java.desktop at 11.0.2/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
     at java.desktop at 11.0.2/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
     at java.desktop at 11.0.2/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
     at java.desktop at 11.0.2/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
     at java.desktop at 11.0.2/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

"JavaFX Application Thread" prio=0 tid=0x0 nid=0x0 runnable
      java.lang.Thread.State: RUNNABLE
  (in native)
     at java.desktop at 11.0.2/sun.lwawt.macosx.LWCToolkit.$$YJP$$doAWTRunLoopImpl(Native 
Method)
     at java.desktop at 11.0.2/sun.lwawt.macosx.LWCToolkit.doAWTRunLoopImpl(LWCToolkit.java)
     at java.desktop at 11.0.2/sun.lwawt.macosx.LWCToolkit.doAWTRunLoop(LWCToolkit.java:1027)
     at java.desktop at 11.0.2/sun.lwawt.macosx.LWCToolkit.invokeAndWait(LWCToolkit.java:827)
     at java.desktop at 11.0.2/sun.lwawt.macosx.LWCToolkit.invokeAndWait(LWCToolkit.java:780)
     at java.desktop at 11.0.2/sun.lwawt.macosx.CInputMethod.characterIndexForPoint(CInputMethod.java:777)

The "characterIndexForPoint" method performs "invokeAndWait" 
 rom JavaFX thread:

             LWCToolkit.invokeAndWait(new Runnable() {
                 public void run() { synchronized(offsetInfo) {
                     offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);
                     insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
                 }}
             }, fAwtFocussedComponent);

which is then on EDT delegates back to JavaFX thread and waits for async result.

It seems a regression of the fix: 
https://hg.openjdk.java.net/openjfx/11/rt/rev/808d535c4e15

Unfortunately, I don't have a simple reproducer. However, the deadlock is obvious from the code.
Comments
A pull request was submitted for review. URL: https://git.openjdk.org/jfx21u/pull/43 Date: 2024-02-20 10:18:36 +0000
20-02-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jfx17u/pull/178 Date: 2024-02-08 09:11:13 +0000
08-02-2024

I want to backport it to JDK17 via https://github.com/openjdk/jfx17u/pull/178, it's already tested and clean
08-02-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jfx/pull/1334 Date: 2024-01-15 16:11:08 +0000
15-01-2024

Changeset: 90cbc663 Author: Kevin Rushforth <kcr@openjdk.org> Date: 2024-01-15 15:33:51 +0000 URL: https://git.openjdk.org/jfx/commit/90cbc66305d0a1380cf3a8cd99ad40db240e554c
15-01-2024

It turns out that we can fix this bug entirely on the JavaFX side. See the description of the PR (listed in the above comment) that is out for review.
10-01-2024

A pull request was submitted for review. URL: https://git.openjdk.org/jfx/pull/1327 Date: 2024-01-10 14:54:47 +0000
10-01-2024

Since the only known way to fix this is in AWT, I have updated the summary along with the affected and fix versions. I took Anton's proposed solution and expanded it to cover all of the invokeAndWait calls in CInputMethod. I have done a lot of testing, and verified that everything now looks good. I have created a Draft PR, and if additional testing looks good, I'll make it "rfr" next week. https://github.com/openjdk/jdk/pull/17290
05-01-2024

On macOS 13 this bug is also easy to reproduce by using Input Method to select another input language. 1. Enable Japanese (or some other language that uses Input Method to enter text) 2. Run the attached test program: $ java HelloWebViewJFXPanel 3. Click on the Yahoo search box (or any other text input element) BUG: Deadlock will occur
22-12-2023

On macOS Sonoma, this is trivially reproducible. 1. Run the attached test program: $ java HelloWebViewJFXPanel 2. Move the mouse into the WebView component in the panel 3. Press the CAPS LOCK key BUG: Deadlock will occur
22-12-2023

A client / AWT PR was submitted for review. Depending on the outcome of the discussion, if the eventual fix is in AWT, then we will need to change the component / subcomponent / affected version accordingly.
22-12-2023

This is now happening much more frequently and consistently on macOS 14 Sonoma. See JDK-8322087 which was just closed as a duplicate of this. I am raising the priority of this bug to P2.
22-12-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/17184 Date: 2023-12-22 11:27:00 +0000
22-12-2023

[~clanger] We don't have anyone looking at this bug. I'm not sure when we'll look at it.
16-11-2022

Hi ([~kcr]?), are there any updates to this case? I was just asked by a customer who is running into this whether there is any update or any activities to implement a fix? Thanks Christoph
14-11-2022

See JDK-8241709 for another test case.
31-03-2020

See JDK-8227829 for another test case that reproduces the deadlock.
17-07-2019

It seems redundant that JavaFX (AppKit) thread invokes a request on EDT that is then delegated back to AppKit. Probably it's worth reducing that turnover to AppKit only. The workaround, suggested above by Arun, works. However, I'd avoid making LWCToolkit.invokeAndWait always run nested loop, but would just introduce a new method version with a boolean param controlling the nested loop run. And then, would modify respectively the calls firstRectForCharacterRange and characterIndexForPoint in CInputMethod: Index: src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java (revision b105cb9dd5dcc7bab2810cf846f581b79063f88e) +++ src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java (date 1557820256111) @@ -758,7 +758,7 @@ } } }} - }, fAwtFocussedComponent); + }, fAwtFocussedComponent, true); } catch (InvocationTargetException ite) { ite.printStackTrace(); } synchronized(rect) { return rect; } @@ -779,7 +779,7 @@ offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY); insertPositionOffset[0] = fIMContext.getInsertPositionOffset(); }} - }, fAwtFocussedComponent); + }, fAwtFocussedComponent, true); } catch (InvocationTargetException ite) { ite.printStackTrace(); } // This bit of gymnastics ensures that the returned location is within the composed text.
14-05-2019

I could workaround this by changing the "LWCToolkit.invokeAndWait" to enter into nested event loop. diff -r 6207397a6603 src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Tue Mar 26 15:00:02 2019 -0700 +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Thu Apr 04 14:10:31 2019 +0530 @@ -691,7 +691,7 @@ SunToolkit.postEvent(appContext, invocationEvent); // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock SunToolkit.flushPendingEvents(appContext); - doAWTRunLoop(mediator, false); + doAWTRunLoop(mediator, true); checkException(invocationEvent); } It is a quick and dirty way to avoid the deadlock. [~kcr], if you feel using the nested event loop is the right approach, then I can formalise the fix and use nested event loop only from CInputMethod.java.
04-04-2019

`characterIndexForPoint` has been called by the native Cocoa framework from the Cocoa main thread(which is also a JavaFX Application Thread). Once the mac implementation of AWT receives the callback it calls relevant AWT methods on EDT and waits for the execution to complete. When WebView implementation of JavaFX receives call to handle the above request, it calls the WebCore APIs on "JavaFX Application Thread" which is already been blocked by AWT. I don't think we can able to find an easy fix for this problem.
04-04-2019

I could reproduce the problem when applying same treatment to `getSelectedText` similar like JDK-8196011. diff --git a/modules/javafx.web/src/main/java/com/sun/javafx/webkit/InputMethodClientImpl.java b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/InputMethodClientImpl.java index 7439dc1a7ae..ba5ff5cac17 100644 --- a/modules/javafx.web/src/main/java/com/sun/javafx/webkit/InputMethodClientImpl.java +++ b/modules/javafx.web/src/main/java/com/sun/javafx/webkit/InputMethodClientImpl.java @@ -166,7 +166,14 @@ public final class InputMethodClientImpl } public String getSelectedText() { - return webPage.getClientSelectedText(); + FutureTask<String> f = new FutureTask<>(() -> { + return webPage.getClientSelectedText(); + }); + try { + Invoker.getInvoker().invokeOnEventThread(f); + return f.get(); + } catch(Exception e) {} + return ""; } @Override `getSelectedText` method is called frequently when using different "Input Source" other than English.
03-04-2019

Hi [~ant], Do you have a full stack trace of "JavaFX Application Thread".?
02-04-2019

Hi Kevin, kindly asking if there's any found solution to this?.. Thank you.
02-04-2019