aboutsummaryrefslogtreecommitdiffstats
path: root/src/nativewindow
diff options
context:
space:
mode:
Diffstat (limited to 'src/nativewindow')
-rw-r--r--src/nativewindow/classes/com/jogamp/nativewindow/awt/AppContextInfo.java199
-rw-r--r--src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java26
2 files changed, 221 insertions, 4 deletions
diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/AppContextInfo.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/AppContextInfo.java
new file mode 100644
index 000000000..54762bb77
--- /dev/null
+++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/AppContextInfo.java
@@ -0,0 +1,199 @@
+package com.jogamp.nativewindow.awt;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import com.jogamp.common.util.RunnableTask;
+
+import jogamp.nativewindow.jawt.JAWTUtil;
+
+/**
+ * Instance of this class holds information about a {@link ThreadGroup} associated {@link sun.awt.AppContext}.
+ * <p>
+ * Non intrusive workaround for Bug 983 and Bug 1004, see {@link #getCachedThreadGroup()}.
+ * </p>
+ */
+public class AppContextInfo {
+ private static final boolean DEBUG;
+
+ private static final Method getAppContextMethod;
+ private static final Object mainThreadAppContextLock = new Object();
+ private volatile WeakReference<Object> mainThreadAppContextWR = null;
+ private volatile WeakReference<ThreadGroup> mainThreadGroupWR = null;
+
+ static {
+ DEBUG = JAWTUtil.DEBUG;
+ final Method[] _getAppContextMethod = { null };
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ try {
+ final Class<?> appContextClass = Class.forName("sun.awt.AppContext");
+ _getAppContextMethod[0] = appContextClass.getMethod("getAppContext");
+ } catch(Throwable ex) {
+ System.err.println("Bug 1004: Catched @ static: "+ex.getMessage());
+ ex.printStackTrace();
+ }
+ return null;
+ } } );
+ getAppContextMethod = _getAppContextMethod[0];
+ }
+
+ public AppContextInfo(final String info) {
+ update(info);
+ }
+
+ /**
+ * Returns <code>true</code> if this instance has valid {@link sun.awt.AppContext} information,
+ * i.e. {@link #getCachedThreadGroup()} returns not <code>null</code>.
+ */
+ public final boolean isValid() {
+ return null != getCachedThreadGroup();
+ }
+
+ /**
+ * Returns the {@link ThreadGroup} belonging to the
+ * last known {@link sun.awt.AppContext} as queried via {@link #update(String)}.
+ * <p>
+ * Returns <code>null</code> if no {@link sun.awt.AppContext} has been queried.
+ * </p>
+ * <p>
+ * The returned {@link ThreadGroup} allows users to create a custom thread
+ * belonging to it and hence mitigating Bug 983 and Bug 1004.
+ * </p>
+ * <p>
+ * {@link #update(String)} should be called from a thread belonging to the
+ * desired {@link sun.awt.AppContext}, i.e. early from within the special threaded application.
+ * </p>
+ * <p>
+ * E.g. {@link JAWTWindow} issues {@link #update(String)} in it's constructor.
+ * </p>
+ */
+ public final ThreadGroup getCachedThreadGroup() {
+ final WeakReference<ThreadGroup> tgRef = mainThreadGroupWR;
+ return null != tgRef ? tgRef.get() : null;
+ }
+
+ /**
+ * Invokes <code>runnable</code> on a {@link Thread} belonging to the {@link sun.awt.AppContext} {@link ThreadGroup},
+ * see {@link #getCachedThreadGroup()}.
+ * <p>
+ * {@link #update(String)} is issued first, which returns <code>true</code>
+ * if the current thread belongs to an AppContext {@link ThreadGroup}.
+ * In this case the <code>runnable</code> is invoked on the current thread,
+ * otherwise a new {@link Thread} will be started.
+ * </p>
+ * <p>
+ * If a new {@link Thread} is required, the AppContext {@link ThreadGroup} is being used
+ * if {@link #isValid() available}, otherwise the default system {@link ThreadGroup}.
+ * </p>
+ *
+ * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately.
+ * @param runnable the {@link Runnable} to be executed. If <code>waitUntilDone</code> is <code>true</code>,
+ * the runnable <b>must exist</b>, i.e. not loop forever.
+ * @param threadBaseName the base name for the new thread if required.
+ * The resulting thread name will have either '-OnAppContextTG' or '-OnSystemTG' appended
+ * @return the {@link Thread} used to invoke the <code>runnable</code>, which may be the current {@link Thread} or a newly created one, see above.
+ */
+ public Thread invokeOnAppContextThread(final boolean waitUntilDone, final Runnable runnable, final String threadBaseName) {
+ final Thread t;
+ if( update("invoke") ) {
+ t = Thread.currentThread();
+ if( DEBUG ) {
+ System.err.println("Bug 1004: Invoke.0 on current AppContext thread: "+t+" "+toHexString(t.hashCode()));
+ }
+ runnable.run();
+ } else {
+ final ThreadGroup tg = getCachedThreadGroup();
+ final String tName = threadBaseName + ( null != tg ? "-OnAppContextTG" : "-OnSystemTG" );
+ t = RunnableTask.invokeOnNewThread(tg, waitUntilDone, runnable, tName);
+ if( DEBUG ) {
+ final int tgHash = null != tg ? tg.hashCode() : 0;
+ System.err.println("Bug 1004: Invoke.1 on new AppContext thread: "+t+" "+toHexString(t.hashCode())+", tg "+tg+" "+toHexString(tgHash));
+ }
+ }
+ return t;
+ }
+
+ /**
+ * Update {@link sun.awt.AppContext} information for the current ThreadGroup if uninitialized or {@link sun.awt.AppContext} changed.
+ * <p>
+ * See {@link #getCachedThreadGroup()} for usage.
+ * </p>
+ * @param info informal string for logging purposes
+ * @return <code>true</code> if the current ThreadGroup is mapped to an {@link sun.awt.AppContext} and the information is good, otherwise false.
+ */
+ public final boolean update(final String info) {
+ if ( null != getAppContextMethod ) {
+ // Test whether the current thread's ThreadGroup is mapped to an AppContext.
+ final Object thisThreadAppContext = fetchAppContext();
+ final boolean tgMapped = null != thisThreadAppContext;
+
+ final Thread thread = Thread.currentThread();
+ final ThreadGroup threadGroup = thread.getThreadGroup();
+ final Object mainThreadAppContext;
+ {
+ final WeakReference<Object> _mainThreadAppContextWR = mainThreadAppContextWR;
+ mainThreadAppContext = null != _mainThreadAppContextWR ? _mainThreadAppContextWR.get() : null;
+ }
+
+ if( tgMapped ) { // null != thisThreadAppContext
+ // Update info is possible
+ if( null == mainThreadAppContext ||
+ mainThreadAppContext != thisThreadAppContext ) {
+ // GC'ed or 1st fetch !
+ final int mainThreadAppContextHash = null != mainThreadAppContext ? mainThreadAppContext.hashCode() : 0;
+ final int thisThreadAppContextHash;
+ synchronized(mainThreadAppContextLock) {
+ mainThreadGroupWR = new WeakReference<ThreadGroup>(threadGroup);
+ mainThreadAppContextWR = new WeakReference<Object>(thisThreadAppContext);
+ thisThreadAppContextHash = thisThreadAppContext.hashCode();
+ }
+ if( DEBUG ) {
+ System.err.println("Bug 1004[TGMapped "+tgMapped+"]: Init AppContext @ "+info+" on thread "+thread.getName()+" "+toHexString(thread.hashCode())+
+ ": tg "+threadGroup.getName()+" "+toHexString(threadGroup.hashCode())+
+ " -> appCtx [ main "+mainThreadAppContext+" "+toHexString(mainThreadAppContextHash)+
+ " -> this "+thisThreadAppContext+" "+toHexString(thisThreadAppContextHash) + " ] ");
+ }
+ } else {
+ // old info is OK
+ if( DEBUG ) {
+ final int mainThreadAppContextHash = null != mainThreadAppContext ? mainThreadAppContext.hashCode() : 0;
+ final int thisThreadAppContextHash = null != thisThreadAppContext ? thisThreadAppContext.hashCode() : 0;
+ System.err.println("Bug 1004[TGMapped "+tgMapped+"]: OK AppContext @ "+info+" on thread "+thread.getName()+" "+toHexString(thread.hashCode())+
+ ": tg "+threadGroup.getName()+" "+toHexString(threadGroup.hashCode())+
+ " : appCtx [ this "+thisThreadAppContext+" "+toHexString(thisThreadAppContextHash)+
+ " , main "+mainThreadAppContext+" "+toHexString(mainThreadAppContextHash) + " ] ");
+ }
+ }
+ return true;
+ } else {
+ if( DEBUG ) {
+ final int mainThreadAppContextHash = null != mainThreadAppContext ? mainThreadAppContext.hashCode() : 0;
+ final int thisThreadAppContextHash = null != thisThreadAppContext ? thisThreadAppContext.hashCode() : 0;
+ System.err.println("Bug 1004[TGMapped "+tgMapped+"]: No AppContext @ "+info+" on thread "+thread.getName()+" "+toHexString(thread.hashCode())+
+ ": tg "+threadGroup.getName()+" "+toHexString(threadGroup.hashCode())+
+ " -> appCtx [ this "+thisThreadAppContext+" "+toHexString(thisThreadAppContextHash)+
+ " -> main "+mainThreadAppContext+" "+toHexString(mainThreadAppContextHash) + " ] ");
+ }
+ }
+ }
+ return false;
+ }
+ private static Object fetchAppContext() {
+ try {
+ return getAppContextMethod.invoke(null);
+ } catch(Exception ex) {
+ System.err.println("Bug 1004: Catched: "+ex.getMessage());
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+ private static String toHexString(final int i) {
+ return "0x"+Integer.toHexString(i);
+ }
+
+}
diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java
index bb7b44795..e2a4ad4bd 100644
--- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java
+++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java
@@ -84,6 +84,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
// lifetime: forever
protected final Component component;
+ private final AppContextInfo appContextInfo;
private final AWTGraphicsConfiguration config; // control access due to delegation
private final SurfaceUpdatedHelper surfaceUpdatedHelper = new SurfaceUpdatedHelper();
private final RecursiveLock surfaceLock = LockFactory.createRecursiveLock();
@@ -114,6 +115,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
if(! ( config instanceof AWTGraphicsConfiguration ) ) {
throw new NativeWindowException("Error: AbstractGraphicsConfiguration is not an AWTGraphicsConfiguration: "+config);
}
+ appContextInfo = new AppContextInfo("<init>");
this.component = (Component)comp;
this.config = (AWTGraphicsConfiguration) config;
this.jawtComponentListener = new JAWTComponentListener();
@@ -121,7 +123,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
this.isApplet = false;
this.offscreenSurfaceLayer = 0;
}
- private static String id(Object obj) { return "0x" + ( null!=obj ? Integer.toHexString(obj.hashCode()) : "nil" ); }
+ private static String id(Object obj) { return ( null!=obj ? toHexString(obj.hashCode()) : "nil" ); }
private String jawtStr() { return "JAWTWindow["+id(JAWTWindow.this)+"]"; }
private class JAWTComponentListener implements ComponentListener, HierarchyListener {
@@ -332,8 +334,20 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
}
attachSurfaceLayerImpl(layerHandle);
offscreenSurfaceLayer = layerHandle;
- component.repaint();
+ appContextInfo.invokeOnAppContextThread(false /* waitUntilDone */, repaintTask, "Repaint");
}
+ private final Runnable repaintTask = new Runnable() {
+ @Override
+ public void run() {
+ final Component c = component;
+ if( DEBUG ) {
+ System.err.println("Bug 1004: RepaintTask on "+Thread.currentThread()+": Has Comp "+(null != c));
+ }
+ if( null != c ) {
+ c.repaint();
+ }
+ } };
+
protected void attachSurfaceLayerImpl(final long layerHandle) {
throw new UnsupportedOperationException("offscreen layer not supported");
}
@@ -744,7 +758,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
append(Platform.JAVA_VERSION_NUMBER).
append(" update ").append(Platform.JAVA_VERSION_UPDATE).append(")").append(Platform.getNewline());
if(null != jawt) {
- sb.append("JAWT version: 0x").append(Integer.toHexString(jawt.getCachedVersion())).
+ sb.append("JAWT version: ").append(toHexString(jawt.getCachedVersion())).
append(", CA_LAYER: ").append(JAWTUtil.isJAWTUsingOffscreenLayer(jawt)).
append(", isLayeredSurface ").append(isOffscreenLayerSurfaceEnabled()).append(", bounds ").append(bounds).append(", insets ").append(insets);
} else {
@@ -775,7 +789,11 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface,
return sb.toString();
}
- protected final String toHexString(long l) {
+ protected static final String toHexString(final long l) {
return "0x"+Long.toHexString(l);
}
+ protected static final String toHexString(final int i) {
+ return "0x"+Integer.toHexString(i);
+ }
+
}