JRE-202 Deadlock in CGLGraphicsConfig.getCGLConfigInfo
Moved getCGLConfigInfo logic execution to AppKit thread so, awt lock is
taken on one thread
diff --git a/src/macosx/classes/sun/font/CStrike.java b/src/macosx/classes/sun/font/CStrike.java
index 896b7cc..9968a38 100644
--- a/src/macosx/classes/sun/font/CStrike.java
+++ b/src/macosx/classes/sun/font/CStrike.java
@@ -461,16 +461,8 @@
// 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
// blocked on RenderQueue.lock
// 1) invokes native block on AppKit and wait
- //
- // If dispatch instance is not available, run the code on
- // disposal thread as before
- final Dispatch dispatch = Dispatch.getInstance();
-
- if (!CThreading.isAppKit() && dispatch != null)
- dispatch.getNonBlockingMainQueueExecutor().execute(command);
- else
- command.run();
+ CThreading.executeOnAppKit(command);
}
private static void disposeLongArray(final long[] longArray) {
diff --git a/src/macosx/classes/sun/font/CStrikeDisposer.java b/src/macosx/classes/sun/font/CStrikeDisposer.java
index 855853f..814a363 100644
--- a/src/macosx/classes/sun/font/CStrikeDisposer.java
+++ b/src/macosx/classes/sun/font/CStrikeDisposer.java
@@ -97,16 +97,8 @@
// 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
// blocked on RenderQueue.lock
// 1) invokes native block on AppKit and wait
- //
- // If dispatch instance is not available, run the code on
- // disposal thread as before
- final Dispatch dispatch = Dispatch.getInstance();
-
- if (!CThreading.isAppKit() && dispatch != null)
- dispatch.getNonBlockingMainQueueExecutor().execute(command);
- else
- command.run();
+ CThreading.executeOnAppKit(command);
}
private native void freeNativeScalerContext(long pContext);
diff --git a/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java b/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
index e98b4da..4a061dd 100644
--- a/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
+++ b/src/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
@@ -26,6 +26,7 @@
package sun.java2d.opengl;
import java.awt.AWTException;
+import java.awt.AWTError;
import java.awt.BufferCapabilities;
import java.awt.Component;
import java.awt.Graphics;
@@ -42,6 +43,7 @@
import java.awt.image.VolatileImage;
import java.awt.image.WritableRaster;
import java.util.HashMap;
+import java.util.concurrent.Callable;
import sun.awt.CGraphicsConfig;
import sun.awt.CGraphicsDevice;
@@ -62,6 +64,7 @@
import sun.lwawt.LWComponentPeer;
import sun.lwawt.macosx.CPlatformView;
+import sun.lwawt.macosx.CThreading;
public final class CGLGraphicsConfig extends CGraphicsConfig
implements OGLGraphicsConfig
@@ -136,40 +139,59 @@
return null;
}
- long cfginfo = 0;
- int textureSize = 0;
- final String ids[] = new String[1];
- OGLRenderQueue rq = OGLRenderQueue.getInstance();
- rq.lock();
- try {
- // getCGLConfigInfo() creates and destroys temporary
- // surfaces/contexts, so we should first invalidate the current
- // Java-level context and flush the queue...
- OGLContext.invalidateCurrentContext();
+ // Move CGLGraphicsConfig creation code to AppKit thread in order to avoid the
+ // following deadlock:
+ // 1) CGLGraphicsConfig.getCGLConfigInfo (called from EDT) takes RenderQueue.lock
+ // 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
+ // blocked on RenderQueue.lock
+ // 1) invokes native block on AppKit and wait
- cfginfo = getCGLConfigInfo(device.getCGDisplayID(), pixfmt,
- kOpenGLSwapInterval);
- if (cfginfo != 0L) {
- textureSize = nativeGetMaxTextureSize();
- // 7160609: GL still fails to create a square texture of this
- // size. Half should be safe enough.
- // Explicitly not support a texture more than 2^14, see 8010999.
- textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
- OGLContext.setScratchSurface(cfginfo);
- rq.flushAndInvokeNow(() -> {
- ids[0] = OGLContext.getOGLIdString();
- });
+ Callable<CGLGraphicsConfig> command = new Callable<CGLGraphicsConfig>() {
+ @Override
+ public CGLGraphicsConfig call() throws Exception {
+ long cfginfo = 0;
+ int textureSize = 0;
+ final String ids[] = new String[1];
+ OGLRenderQueue rq = OGLRenderQueue.getInstance();
+ rq.lock();
+ try {
+ // getCGLConfigInfo() creates and destroys temporary
+ // surfaces/contexts, so we should first invalidate the current
+ // Java-level context and flush the queue...
+ OGLContext.invalidateCurrentContext();
+
+ cfginfo = getCGLConfigInfo(device.getCGDisplayID(), pixfmt,
+ kOpenGLSwapInterval);
+ if (cfginfo != 0L) {
+ textureSize = nativeGetMaxTextureSize();
+ // 7160609: GL still fails to create a square texture of this
+ // size. Half should be safe enough.
+ // Explicitly not support a texture more than 2^14, see 8010999.
+ textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
+ OGLContext.setScratchSurface(cfginfo);
+ rq.flushAndInvokeNow(() -> {
+ ids[0] = OGLContext.getOGLIdString();
+ });
+ }
+ } finally {
+ rq.unlock();
+ }
+ if (cfginfo == 0) {
+ return null;
+ }
+
+ int oglCaps = getOGLCapabilities(cfginfo);
+ ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]);
+ return new CGLGraphicsConfig(
+ device, pixfmt, cfginfo, textureSize, caps);
}
- } finally {
- rq.unlock();
- }
- if (cfginfo == 0) {
- return null;
- }
+ };
- int oglCaps = getOGLCapabilities(cfginfo);
- ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]);
- return new CGLGraphicsConfig(device, pixfmt, cfginfo, textureSize, caps);
+ try {
+ return CThreading.executeOnAppKit(command);
+ } catch (Throwable throwable) {
+ throw new AWTError(throwable.getMessage());
+ }
}
static void refPConfigInfo(long pConfigInfo) {
diff --git a/src/macosx/classes/sun/lwawt/macosx/CThreading.java b/src/macosx/classes/sun/lwawt/macosx/CThreading.java
index b3fc716..f9df79b 100644
--- a/src/macosx/classes/sun/lwawt/macosx/CThreading.java
+++ b/src/macosx/classes/sun/lwawt/macosx/CThreading.java
@@ -24,17 +24,23 @@
*/
package sun.lwawt.macosx;
+import com.apple.concurrent.Dispatch;
+
import java.awt.EventQueue;
+import java.awt.AWTError;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
public class CThreading {
- static String APPKIT_THREAD_NAME = "AppKit Thread";
+ static String APPKIT_THREAD_NAME = "AWT-AppKit";
static boolean isEventQueue() {
return EventQueue.isDispatchThread();
}
- public static boolean isAppKit() {
+ private static boolean isAppKit() {
return APPKIT_THREAD_NAME.equals(Thread.currentThread().getName());
}
@@ -61,4 +67,41 @@
assert isNotAppKitThread : "Threading violation: AppKit thread";
return isNotAppKitThread;
}
+
+ public static <V> V executeOnAppKit(Callable<V> command) throws Throwable {
+ if (!isAppKit()) {
+ Dispatch dispatch = Dispatch.getInstance();
+
+ if (dispatch == null) {
+ throw new AWTError("Could not get Dispatch object");
+ }
+
+ FutureTask<V> future = new FutureTask<>(command);
+
+ dispatch.getNonBlockingMainQueueExecutor().execute(future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException e) {
+ throw new AWTError(e.getMessage());
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+ } else
+ return command.call();
+ }
+
+ public static void executeOnAppKit(Runnable command) {
+ if (!isAppKit()) {
+ Dispatch dispatch = Dispatch.getInstance();
+
+ if (dispatch != null) {
+ dispatch.getNonBlockingMainQueueExecutor().execute(command);
+ }
+ else {
+ throw new AWTError("Could not get Dispatch object");
+ }
+ } else
+ command.run();
+ }
}