JRE-373 [macos] nativeCreateNSWindow deadlocks with a11y
diff --git a/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
index e879070..afa8c20 100644
--- a/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
+++ b/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
@@ -35,6 +35,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Callable;
import javax.swing.*;
@@ -249,8 +250,10 @@
} else {
bounds = _peer.constrainBounds(_target.getBounds());
}
- final long nativeWindowPtr = nativeCreateNSWindow(contentView.getAWTView(),
- ownerPtr, styleBits, bounds.x, bounds.y, bounds.width, bounds.height);
+ long nativeWindowPtr = LWCToolkit.performOnMainThreadWaiting(() ->
+ nativeCreateNSWindow(contentView.getAWTView(),
+ ownerPtr, styleBits,
+ bounds.x, bounds.y, bounds.width, bounds.height));
setPtr(nativeWindowPtr);
if (target instanceof javax.swing.RootPaneContainer) {
diff --git a/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java b/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
index 654fbc3..36d97cf 100644
--- a/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
+++ b/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
@@ -39,7 +39,7 @@
import java.net.URL;
import java.security.*;
import java.util.*;
-import java.util.concurrent.Callable;
+import java.util.concurrent.*;
import java.net.MalformedURLException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -634,6 +634,51 @@
}
}
+ private static ExecutorService onMainThreadExecutor;
+
+ /**
+ * Performs the callable on the main thread running a secondary loop on EDT while waiting for the result.
+ * <p>
+ * The callable should delegate to the native method which is expected to execute
+ * [JNFRunLoop performOnMainThreadWaiting:YES ...]. Note that the native doAWTRunLoop runs in
+ * [JNFRunLoop javaRunLoopMode] which accepts selectors of the kind. The callable should not execute
+ * any Java code which would normally be executed on EDT.
+ * <p>
+ * The method must be called on EDT. It's reentrant.
+ */
+ public static <T> T performOnMainThreadWaiting(Callable<T> callable) {
+ if (!EventQueue.isDispatchThread()) {
+ throw new RuntimeException("the method must be called on the Event Dispatching thread");
+ }
+ if (callable == null) return null;
+
+ if (onMainThreadExecutor == null) {
+ // init on EDT
+ onMainThreadExecutor = new ThreadPoolExecutor(1, Integer.MAX_VALUE,
+ 60L, TimeUnit.SECONDS,
+ new SynchronousQueue<>(),
+ Executors.privilegedThreadFactory());
+
+ }
+ Future<T> task = onMainThreadExecutor.submit(() -> {
+ T res = callable.call();
+ EventQueue.invokeLater(() -> {/* wake up EDT */});
+ return res;
+ });
+
+ AWTAccessor.getEventQueueAccessor().createSecondaryLoop(
+ Toolkit.getDefaultToolkit().getSystemEventQueue(),
+ () -> !task.isDone()).enter();
+
+ try {
+ return task.get();
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
/**
* Kicks an event over to the appropriate event queue and waits for it to
* finish To avoid deadlocking, we manually run the NSRunLoop while waiting