diff options
author | Sven Gothel <[email protected]> | 2012-09-30 19:47:46 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-09-30 19:47:46 +0200 |
commit | 92398025abdabb2fdef0d78edd41e730991a6f94 (patch) | |
tree | 8d209c588d1246aa126c29465206ba1f8167c0e2 /src/nativewindow/classes | |
parent | 43891be36e8485353ac74f329fd2f7438303a846 (diff) |
Workaround for Bug 623: Sporadic XCB assertion failures w/ ATI proprietary driver and w/o native X11 locking
The proprietary ATI X11 driver does not handle multi-threaded [GL] clients well,
i.e. triggers an XCB assertion 'from time to time'.
It almost seems like that the driver either:
- aliases all display connections to it's connection name, i.e. server; or
- utilizes a build-in display connection w/o locking, used for some reason
+++
- X11Lib: Add QueryExtension(dpy, name) allowing early driver determination w/o GL
- X11Util detects 'requiresGlobalToolkitLock' and 'markAllDisplaysUnclosable' via
X11 extensions. In case certain ATI extensions are available, both are set to true.
- X11GLXDrawableFactory: Dropped setting 'markAllDisplaysUnclosable', using X11Util's detection (see above).
- New GlobalToolkitLock to satisfy certain driver restrictions (ATI's XCB multithreading bug)
- NativeWindowFactory handles new property requiresGlobalToolkitLock,
in which case the new GlobalToolkitLock is being used instead of ResourceToolkitLock.
- JAWTUtil ToolkitLock locks GlobalToolkitLock 1st to match new 'requiresGlobalToolkitLock' property.
- Document static method requirement of X11Util, GDIUtil and OSXUtil via marker interface ToolkitProperties
- ToolkitLock: New method 'validateLocked()', allowing use to validate whether the device/toolkit
is properly locked and hence to detect implementation bugs. See unit test class: ValidateLockListener
Diffstat (limited to 'src/nativewindow/classes')
15 files changed, 378 insertions, 81 deletions
diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java b/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java index 142bb99e3..0f28ca67c 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/x11/X11GraphicsDevice.java @@ -61,7 +61,7 @@ public class X11GraphicsDevice extends DefaultGraphicsDevice implements Cloneabl } /** Constructs a new X11GraphicsDevice corresponding to the given native display handle and default - * {@link javax.media.nativewindow.ToolkitLock} via {@link NativeWindowFactory#createDefaultToolkitLock(String, long)}. + * {@link javax.media.nativewindow.ToolkitLock} via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. * @see DefaultGraphicsDevice#DefaultGraphicsDevice(String, String, int, long) */ public X11GraphicsDevice(long display, int unitID, boolean owner) { diff --git a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java index 5eaaa6613..8ecd5242d 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java +++ b/src/nativewindow/classes/javax/media/nativewindow/AbstractGraphicsDevice.java @@ -102,17 +102,24 @@ public interface AbstractGraphicsDevice extends Cloneable { public long getHandle(); /** - * Optionally locking the device, utilizing eg {@link javax.media.nativewindow.ToolkitLock}. + * Optionally locking the device, utilizing eg {@link javax.media.nativewindow.ToolkitLock#lock()}. * The lock implementation must be recursive. */ public void lock(); /** - * Optionally unlocking the device, utilizing eg {@link javax.media.nativewindow.ToolkitLock}. + * Optionally unlocking the device, utilizing eg {@link javax.media.nativewindow.ToolkitLock#unlock()}. * The lock implementation must be recursive. + * + * @throws RuntimeException in case the lock is not acquired by this thread. */ public void unlock(); + /** + * @throws RuntimeException if current thread does not hold the lock + */ + public void validateLocked() throws RuntimeException; + /** * Optionally [re]opening the device if handle is <code>null</code>. * <p> diff --git a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java index b16b0c75c..9288652d9 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java +++ b/src/nativewindow/classes/javax/media/nativewindow/DefaultGraphicsDevice.java @@ -60,7 +60,7 @@ public class DefaultGraphicsDevice implements Cloneable, AbstractGraphicsDevice /** * Create an instance with the system default {@link ToolkitLock}. - * gathered via {@link NativeWindowFactory#createDefaultToolkitLock(String, long)}. + * gathered via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. * @param type * @param handle */ @@ -70,7 +70,7 @@ public class DefaultGraphicsDevice implements Cloneable, AbstractGraphicsDevice this.unitID = unitID; this.uniqueID = getUniqueID(type, connection, unitID); this.handle = handle; - this.toolkitLock = NativeWindowFactory.createDefaultToolkitLock(type, handle); + this.toolkitLock = NativeWindowFactory.getDefaultToolkitLock(type, handle); } /** @@ -123,8 +123,10 @@ public class DefaultGraphicsDevice implements Cloneable, AbstractGraphicsDevice } /** - * No lock is performed on the graphics device per default, - * instead the aggregated recursive {@link ToolkitLock#lock()} is invoked. + * {@inheritDoc} + * <p> + * Locking is perfomed via delegation to {@link ToolkitLock#lock()}, {@link ToolkitLock#unlock()}. + * </p> * * @see DefaultGraphicsDevice#DefaultGraphicsDevice(java.lang.String, long) * @see DefaultGraphicsDevice#DefaultGraphicsDevice(java.lang.String, long, javax.media.nativewindow.ToolkitLock) @@ -134,9 +136,16 @@ public class DefaultGraphicsDevice implements Cloneable, AbstractGraphicsDevice toolkitLock.lock(); } + @Override + public final void validateLocked() throws RuntimeException { + toolkitLock.validateLocked(); + } + /** - * No lock is performed on the graphics device per default, - * instead the aggregated recursive {@link ToolkitLock#unlock()} is invoked. + * {@inheritDoc} + * <p> + * Locking is perfomed via delegation to {@link ToolkitLock#lock()}, {@link ToolkitLock#unlock()}. + * </p> * * @see DefaultGraphicsDevice#DefaultGraphicsDevice(java.lang.String, long) * @see DefaultGraphicsDevice#DefaultGraphicsDevice(java.lang.String, long, javax.media.nativewindow.ToolkitLock) diff --git a/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java b/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java index 4f4bb629b..af8008f0f 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java +++ b/src/nativewindow/classes/javax/media/nativewindow/NativeWindowFactory.java @@ -42,6 +42,7 @@ import java.util.Map; import jogamp.nativewindow.Debug; import jogamp.nativewindow.NativeWindowFactoryImpl; +import jogamp.nativewindow.ToolkitProperties; import jogamp.nativewindow.ResourceToolkitLock; import com.jogamp.common.os.Platform; @@ -87,13 +88,17 @@ public abstract class NativeWindowFactory { private static boolean isAWTAvailable; private static final String JAWTUtilClassName = "jogamp.nativewindow.jawt.JAWTUtil" ; + /** {@link jogamp.nativewindow.x11.X11Util} implements {@link ToolkitProperties}. */ private static final String X11UtilClassName = "jogamp.nativewindow.x11.X11Util"; + /** {@link jogamp.nativewindow.macosx.OSXUtil} implements {@link ToolkitProperties}. */ private static final String OSXUtilClassName = "jogamp.nativewindow.macosx.OSXUtil"; + /** {@link jogamp.nativewindow.windows.GDIUtil} implements {@link ToolkitProperties}. */ private static final String GDIClassName = "jogamp.nativewindow.windows.GDIUtil"; private static ToolkitLock jawtUtilJAWTToolkitLock; private static boolean requiresToolkitLock; + private static boolean requiresGlobalToolkitLock; private static volatile boolean isJVMShuttingDown = false; @@ -156,11 +161,18 @@ public abstract class NativeWindowFactory { if( null != clazzName ) { ReflectionUtil.callStaticMethod(clazzName, "initSingleton", null, null, cl ); - final Boolean res = (Boolean) ReflectionUtil.callStaticMethod(clazzName, "requiresToolkitLock", null, null, cl); - requiresToolkitLock = res.booleanValue(); + final Boolean res1 = (Boolean) ReflectionUtil.callStaticMethod(clazzName, "requiresToolkitLock", null, null, cl); + requiresToolkitLock = res1.booleanValue(); + if(requiresToolkitLock) { + final Boolean res2 = (Boolean) ReflectionUtil.callStaticMethod(clazzName, "requiresGlobalToolkitLock", null, null, cl); + requiresGlobalToolkitLock = res2.booleanValue(); + } else { + requiresGlobalToolkitLock = false; + } } else { requiresToolkitLock = false; - } + requiresGlobalToolkitLock = false; + } } private static void shutdownNativeImpl(final ClassLoader cl) { @@ -261,7 +273,7 @@ public abstract class NativeWindowFactory { } if(DEBUG) { - System.err.println("NativeWindowFactory requiresToolkitLock "+requiresToolkitLock); + System.err.println("NativeWindowFactory requiresToolkitLock "+requiresToolkitLock+", requiresGlobalToolkitLock "+requiresGlobalToolkitLock); System.err.println("NativeWindowFactory isAWTAvailable "+isAWTAvailable+", defaultFactory "+factory); } @@ -295,7 +307,12 @@ public abstract class NativeWindowFactory { /** @return true if the underlying toolkit requires locking, otherwise false. */ public static boolean requiresToolkitLock() { return requiresToolkitLock; - } + } + + /** @return true if the underlying toolkit requires global locking, otherwise false. */ + public static boolean requiresGlobalToolkitLock() { + return requiresGlobalToolkitLock; + } /** @return true if not headless, AWT Component and NativeWindow's AWT part available */ public static boolean isAWTAvailable() { return isAWTAvailable; } @@ -331,9 +348,28 @@ public abstract class NativeWindowFactory { return defaultFactory; } + /** + * Returns the AWT {@link ToolkitLock} (JAWT based) if {@link #isAWTAvailable}, otherwise null. + * <p> + * The JAWT based {@link ToolkitLock} also locks the global lock, + * which matters if the latter is required. + * </p> + */ + public static ToolkitLock getAWTToolkitLock() { + return jawtUtilJAWTToolkitLock; + } + + public static ToolkitLock getNullToolkitLock() { + return NativeWindowFactoryImpl.getNullToolkitLock(); + } + + public static ToolkitLock getGlobalToolkitLock() { + return NativeWindowFactoryImpl.getGlobalToolkitLock(); + } + /** - * Provides the system default {@link ToolkitLock}, a singleton instance. - * <br> + * Provides the system default {@link ToolkitLock} for the default system windowing type. + * @see #getNativeWindowType(boolean) * @see #getDefaultToolkitLock(java.lang.String) */ public static ToolkitLock getDefaultToolkitLock() { @@ -341,57 +377,44 @@ public abstract class NativeWindowFactory { } /** - * Provides the default {@link ToolkitLock} for <code>type</code>, a singleton instance. + * Provides the default {@link ToolkitLock} for <code>type</code>. * <ul> - * <li> JAWT {@link ToolkitLock} if required and AWT available, otherwise</li> + * <li> JAWT {@link ToolkitLock} if required and <code>type</code> is of {@link #TYPE_AWT} and AWT available,</li> + * <li> {@link jogamp.nativewindow.GlobalToolkitLock} if required, otherwise</li> + * <li> {@link jogamp.nativewindow.ResourceToolkitLock} if required, otherwise</li> * <li> {@link jogamp.nativewindow.NullToolkitLock} </li> * </ul> */ public static ToolkitLock getDefaultToolkitLock(String type) { - if( requiresToolkitLock() ) { - if( TYPE_AWT == type && TYPE_X11 == getNativeWindowType(false) && isAWTAvailable() ) { + if( requiresToolkitLock ) { + if( TYPE_AWT == type && isAWTAvailable() ) { return getAWTToolkitLock(); } - } - return NativeWindowFactoryImpl.getNullToolkitLock(); - } - - /** Returns the AWT Toolkit (JAWT based) if {@link #isAWTAvailable}, otherwise null. */ - public static ToolkitLock getAWTToolkitLock() { - return jawtUtilJAWTToolkitLock; - } - - public static ToolkitLock getNullToolkitLock() { - return NativeWindowFactoryImpl.getNullToolkitLock(); - } - - /** - * Creates the default {@link ToolkitLock} for <code>type</code> and <code>deviceHandle</code>. - * <ul> - * <li> {@link jogamp.nativewindow.ResourceToolkitLock} if required, otherwise</li> - * <li> {@link jogamp.nativewindow.NullToolkitLock} </li> - * </ul> - */ - public static ToolkitLock createDefaultToolkitLock(String type, long deviceHandle) { - if( requiresToolkitLock() ) { + if( requiresGlobalToolkitLock ) { + return NativeWindowFactoryImpl.getGlobalToolkitLock(); + } return ResourceToolkitLock.create(); } return NativeWindowFactoryImpl.getNullToolkitLock(); } - + /** - * Creates the default {@link ToolkitLock} for <code>type</code> and <code>deviceHandle</code>. + * Provides the default {@link ToolkitLock} for <code>type</code> and <code>deviceHandle</code>. * <ul> - * <li> JAWT {@link ToolkitLock} if required and AWT available,</li> + * <li> JAWT {@link ToolkitLock} if required and <code>type</code> is of {@link #TYPE_AWT} and AWT available,</li> + * <li> {@link jogamp.nativewindow.GlobalToolkitLock} if required, otherwise</li> * <li> {@link jogamp.nativewindow.ResourceToolkitLock} if required, otherwise</li> * <li> {@link jogamp.nativewindow.NullToolkitLock} </li> * </ul> */ - public static ToolkitLock createDefaultToolkitLock(String type, String sharedType, long deviceHandle) { - if( requiresToolkitLock() ) { - if( TYPE_AWT == sharedType && isAWTAvailable() ) { + public static ToolkitLock getDefaultToolkitLock(String type, long deviceHandle) { + if( requiresToolkitLock ) { + if( TYPE_AWT == type && isAWTAvailable() ) { return getAWTToolkitLock(); } + if( requiresGlobalToolkitLock ) { + return NativeWindowFactoryImpl.getGlobalToolkitLock(); + } return ResourceToolkitLock.create(); } return NativeWindowFactoryImpl.getNullToolkitLock(); diff --git a/src/nativewindow/classes/javax/media/nativewindow/ToolkitLock.java b/src/nativewindow/classes/javax/media/nativewindow/ToolkitLock.java index 18b7cf5d9..eccfcfa4f 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/ToolkitLock.java +++ b/src/nativewindow/classes/javax/media/nativewindow/ToolkitLock.java @@ -37,15 +37,35 @@ import jogamp.nativewindow.Debug; * Toolkit locks are created solely via {@link NativeWindowFactory}. * </p> * <p> - * One use case is the AWT locking on X11, see {@link NativeWindowFactory#createDefaultToolkitLock(String, long)}. + * One use case is the AWT locking on X11, see {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. * </p> */ public interface ToolkitLock { + public static final boolean DEBUG = Debug.debug("ToolkitLock"); public static final boolean TRACE_LOCK = Debug.isPropertyDefined("nativewindow.debug.ToolkitLock.TraceLock", true); + /** + * Blocking until the lock is acquired by this Thread or a timeout is reached. + * <p> + * Timeout is implementation specific, if used at all. + * </p> + * + * @throws RuntimeException in case of a timeout + */ public void lock(); + + /** + * Release the lock. + * + * @throws RuntimeException in case the lock is not acquired by this thread. + */ public void unlock(); + /** + * @throws RuntimeException if current thread does not hold the lock + */ + public void validateLocked() throws RuntimeException; + /** * Dispose this instance. * <p> diff --git a/src/nativewindow/classes/jogamp/nativewindow/GlobalToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/GlobalToolkitLock.java new file mode 100644 index 000000000..0c2a1e43f --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/GlobalToolkitLock.java @@ -0,0 +1,75 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.nativewindow; + +import javax.media.nativewindow.ToolkitLock; + +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; + +/** + * Implementing a global recursive {@link javax.media.nativewindow.ToolkitLock}. + * <p> + * This is the last resort for unstable driver, e.g. proprietary ATI/X11 12.8 and 12.9, + * where multiple X11 display connections to the same connection name are not treated + * thread safe within the GL/X11 driver. + * </p> + */ +public class GlobalToolkitLock implements ToolkitLock { + private static final RecursiveLock globalLock = LockFactory.createRecursiveLock(); + + /** Singleton via {@link NativeWindowFactoryImpl#getGlobalToolkitLock()} */ + protected GlobalToolkitLock() { } + + @Override + public final void lock() { + globalLock.lock(); + if(TRACE_LOCK) { System.err.println("GlobalToolkitLock.lock()"); } + } + + @Override + public final void unlock() { + if(TRACE_LOCK) { System.err.println("GlobalToolkitLock.unlock()"); } + globalLock.unlock(); // implicit lock validation + } + + @Override + public final void validateLocked() throws RuntimeException { + globalLock.validateLocked(); + } + + @Override + public final void dispose() { + // nop + } + + public String toString() { + return "GlobalToolkitLock[obj 0x"+Integer.toHexString(hashCode())+", isOwner "+globalLock.isOwner(Thread.currentThread())+", "+globalLock.toString()+"]"; + } +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java b/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java index 29564da3b..c35cede77 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java +++ b/src/nativewindow/classes/jogamp/nativewindow/NativeWindowFactoryImpl.java @@ -44,9 +44,14 @@ import com.jogamp.common.util.ReflectionUtil.AWTNames; public class NativeWindowFactoryImpl extends NativeWindowFactory { private static final ToolkitLock nullToolkitLock = new NullToolkitLock(); + private static final ToolkitLock globalToolkitLock = new GlobalToolkitLock(); public static ToolkitLock getNullToolkitLock() { - return nullToolkitLock; + return nullToolkitLock; + } + + public static ToolkitLock getGlobalToolkitLock() { + return globalToolkitLock; } // This subclass of NativeWindowFactory handles the case of diff --git a/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java index e59910138..211e15955 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java +++ b/src/nativewindow/classes/jogamp/nativewindow/NullToolkitLock.java @@ -31,15 +31,16 @@ package jogamp.nativewindow; import javax.media.nativewindow.ToolkitLock; /** - * Implementing a singleton global recursive {@link javax.media.nativewindow.ToolkitLock} - * without any locking. Since there is no locking it all, - * it is intrinsically recursive. + * Implementing a singleton global NOP {@link javax.media.nativewindow.ToolkitLock} + * without any locking. Since there is no locking it all, it is intrinsically recursive. */ public class NullToolkitLock implements ToolkitLock { - + public static final boolean INVALID_LOCKED = Debug.isPropertyDefined("nativewindow.debug.NullToolkitLock.InvalidLocked", true); + /** Singleton via {@link NativeWindowFactoryImpl#getNullToolkitLock()} */ protected NullToolkitLock() { } + @Override public final void lock() { if(TRACE_LOCK) { System.err.println("NullToolkitLock.lock()"); @@ -47,10 +48,20 @@ public class NullToolkitLock implements ToolkitLock { } } + @Override public final void unlock() { if(TRACE_LOCK) { System.err.println("NullToolkitLock.unlock()"); } } + @Override + public final void validateLocked() throws RuntimeException { + /* nop */ + if(INVALID_LOCKED) { + throw new RuntimeException("NullToolkitLock does not lock"); + } + } + + @Override public final void dispose() { // nop } diff --git a/src/nativewindow/classes/jogamp/nativewindow/ResourceToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/ResourceToolkitLock.java index a3b0804fa..5b79de0b8 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/ResourceToolkitLock.java +++ b/src/nativewindow/classes/jogamp/nativewindow/ResourceToolkitLock.java @@ -41,8 +41,6 @@ import com.jogamp.common.util.locks.RecursiveLock; * </p> */ public class ResourceToolkitLock implements ToolkitLock { - public static final boolean DEBUG = Debug.debug("ToolkitLock"); - public static final ResourceToolkitLock create() { return new ResourceToolkitLock(); } @@ -53,17 +51,24 @@ public class ResourceToolkitLock implements ToolkitLock { this.lock = LockFactory.createRecursiveLock(); } - + @Override public final void lock() { - if(TRACE_LOCK) { System.err.println("ResourceToolkitLock.lock()"); } lock.lock(); + if(TRACE_LOCK) { System.err.println("ResourceToolkitLock.lock()"); } } + @Override public final void unlock() { if(TRACE_LOCK) { System.err.println("ResourceToolkitLock.unlock()"); } - lock.unlock(); + lock.unlock(); // implicit lock validation + } + + @Override + public final void validateLocked() throws RuntimeException { + lock.validateLocked(); } + @Override public final void dispose() { // nop } diff --git a/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java index 5d7ae8abb..94d12e6fc 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java +++ b/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java @@ -49,7 +49,6 @@ import com.jogamp.common.util.locks.RecursiveLock; * </p> */ public class SharedResourceToolkitLock implements ToolkitLock { - public static final boolean DEBUG = Debug.debug("ToolkitLock"); private static final LongObjectHashMap handle2Lock; static { handle2Lock = new LongObjectHashMap(); @@ -109,16 +108,24 @@ public class SharedResourceToolkitLock implements ToolkitLock { } + @Override public final void lock() { - if(TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.lock()"); } lock.lock(); + if(TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.lock()"); } } + @Override public final void unlock() { if(TRACE_LOCK) { System.err.println("SharedResourceToolkitLock.unlock()"); } lock.unlock(); } + @Override + public final void validateLocked() throws RuntimeException { + lock.validateLocked(); + } + + @Override public final void dispose() { if(0 < refCount) { // volatile OK synchronized(handle2Lock) { diff --git a/src/nativewindow/classes/jogamp/nativewindow/ToolkitProperties.java b/src/nativewindow/classes/jogamp/nativewindow/ToolkitProperties.java new file mode 100644 index 000000000..2062d1f58 --- /dev/null +++ b/src/nativewindow/classes/jogamp/nativewindow/ToolkitProperties.java @@ -0,0 +1,51 @@ +package jogamp.nativewindow; + +import javax.media.nativewindow.NativeWindowFactory; + +/** + * Marker interface. + * <p> + * Implementation requires to provide static methods: + * <pre> + public static void initSingleton() {} + + public static void shutdown() {} + + public static boolean requiresToolkitLock() {} + + public static boolean requiresGlobalToolkitLock() {} + * </pre> + * Above static methods are invoked by {@link NativeWindowFactory#initSingleton()}, + * or {@link NativeWindowFactory#shutdown()} via reflection. + * </p> + * <p> + * If <code>requiresGlobalToolkitLock() == true</code>, then + * <code>requiresToolkitLock() == true</code> shall be valid as well. + * </p> + */ +public interface ToolkitProperties { + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + */ + // void initSingleton(); + + /** + * Cleanup resources. + * <p> + * Called by {@link NativeWindowFactory#shutdown()} + * </p> + */ + // void shutdown(); + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + */ + // boolean requiresToolkitLock(); + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + */ + // boolean requiresGlobalToolkitLock(); + +} diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java index 7c934b154..2dadfb16b 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/JAWTUtil.java @@ -79,6 +79,8 @@ public class JAWTUtil { private static final Method sunToolkitAWTLockMethod; private static final Method sunToolkitAWTUnlockMethod; private static final boolean hasSunToolkitAWTLock; + + private static volatile Thread exclusiveOwnerThread; private static final ToolkitLock jawtToolkitLock; @@ -229,13 +231,23 @@ public class JAWTUtil { } hasSunToolkitAWTLock = _hasSunToolkitAWTLock; // hasSunToolkitAWTLock = false; + exclusiveOwnerThread = null; jawtToolkitLock = new ToolkitLock() { public final void lock() { + NativeWindowFactory.getGlobalToolkitLock().lock(); JAWTUtil.lockToolkit(); + if(TRACE_LOCK) { System.err.println("JAWTToolkitLock.lock()"); } } public final void unlock() { + if(TRACE_LOCK) { System.err.println("JAWTToolkitLock.unlock()"); } JAWTUtil.unlockToolkit(); + NativeWindowFactory.getGlobalToolkitLock().unlock(); + } + @Override + public final void validateLocked() throws RuntimeException { + NativeWindowFactory.getGlobalToolkitLock().validateLocked(); + JAWTUtil.validateLocked(); } public final void dispose() { // nop @@ -312,7 +324,7 @@ public class JAWTUtil { * JAWT's native Lock() function calls SunToolkit.awtLock(), * which just uses AWT's global ReentrantLock.<br> */ - private static void awtLock() { + private static final void awtLock() { if(hasSunToolkitAWTLock) { try { sunToolkitAWTLockMethod.invoke(null, (Object[])null); @@ -322,6 +334,7 @@ public class JAWTUtil { } else { jawtLockObject.Lock(); } + exclusiveOwnerThread = Thread.currentThread(); } /** @@ -330,7 +343,9 @@ public class JAWTUtil { * JAWT's native Unlock() function calls SunToolkit.awtUnlock(), * which just uses AWT's global ReentrantLock.<br> */ - private static void awtUnlock() { + private static final void awtUnlock() { + awtValidateLocked(); + exclusiveOwnerThread = null; if(hasSunToolkitAWTLock) { try { sunToolkitAWTUnlockMethod.invoke(null, (Object[])null); @@ -342,6 +357,16 @@ public class JAWTUtil { } } + private static final void awtValidateLocked() throws RuntimeException { + final Thread ct = Thread.currentThread(); + if( ct != exclusiveOwnerThread ) { + if ( null == exclusiveOwnerThread ) { + throw new RuntimeException(ct.getName()+": JAWT-ToolkitLock not locked"); + } + throw new RuntimeException(ct.getName()+": Not JAWT-ToolkitLock owner. Owner is "+exclusiveOwnerThread.getName()); + } + } + public static void lockToolkit() throws NativeWindowException { if(ToolkitLock.TRACE_LOCK) { System.err.println("JAWTUtil-ToolkitLock.lock()"); } if(!headlessMode && !isJava2DQueueFlusherThread()) { @@ -355,6 +380,13 @@ public class JAWTUtil { awtUnlock(); } } + + public static final void validateLocked() throws RuntimeException { + if(!headlessMode && !isJava2DQueueFlusherThread()) { + awtValidateLocked(); + } + } + public static ToolkitLock getJAWTToolkitLock() { return jawtToolkitLock; diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index b7a83e133..481cbbe39 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -34,13 +34,15 @@ import javax.media.nativewindow.util.Point; import jogamp.nativewindow.Debug; import jogamp.nativewindow.NWJNILibLoader; +import jogamp.nativewindow.ToolkitProperties; -public class OSXUtil { +public class OSXUtil implements ToolkitProperties { private static boolean isInit = false; private static final boolean DEBUG = Debug.debug("OSXUtil"); /** * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties */ public static synchronized void initSingleton() { if(!isInit) { @@ -60,13 +62,21 @@ public class OSXUtil { /** * Called by {@link NativeWindowFactory#shutdown()} + * @see ToolkitProperties */ - public static void shutdown() { - } + public static void shutdown() { } - public static boolean requiresToolkitLock() { - return false; - } + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static boolean requiresToolkitLock() { return false; } + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean requiresGlobalToolkitLock() { return false; } public static boolean isNSView(long object) { return isNSView0(object); diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java index 613c76032..4408a0903 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java @@ -33,9 +33,10 @@ import javax.media.nativewindow.NativeWindowFactory; import jogamp.nativewindow.NWJNILibLoader; import jogamp.nativewindow.Debug; +import jogamp.nativewindow.ToolkitProperties; import jogamp.nativewindow.x11.X11Util; -public class GDIUtil { +public class GDIUtil implements ToolkitProperties { private static final boolean DEBUG = Debug.debug("GDIUtil"); private static final String dummyWindowClassNameBase = "_dummyWindow_clazz" ; @@ -44,6 +45,7 @@ public class GDIUtil { /** * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties */ public static synchronized void initSingleton() { if(!isInit) { @@ -67,12 +69,23 @@ public class GDIUtil { /** * Called by {@link NativeWindowFactory#shutdown()} + * @see ToolkitProperties */ public static void shutdown() { } + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ public static boolean requiresToolkitLock() { return false; } + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean requiresGlobalToolkitLock() { return false; } + private static RegisteredClass dummyWindowClass = null; private static Object dummyWindowSync = new Object(); diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java index 30792113d..a331fa940 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java @@ -43,13 +43,15 @@ import javax.media.nativewindow.NativeWindowFactory; import jogamp.nativewindow.Debug; import jogamp.nativewindow.NWJNILibLoader; +import jogamp.nativewindow.ToolkitProperties; + import com.jogamp.common.util.LongObjectHashMap; import com.jogamp.nativewindow.x11.X11GraphicsDevice; /** * Contains a thread safe X11 utility to retrieve display connections. */ -public class X11Util { +public class X11Util implements ToolkitProperties { /** * See Bug 515 - https://jogamp.org/bugzilla/show_bug.cgi?id=515 * <p> @@ -88,11 +90,15 @@ public class X11Util { private static String nullDisplayName = null; private static volatile boolean isInit = false; private static boolean markAllDisplaysUnclosable = false; // ATI/AMD X11 driver issues + private static boolean requiresGlobalToolkitLock = false; // ATI/AMD X11 driver issues private static Object setX11ErrorHandlerLock = new Object(); + private static final String X11_EXTENSION_ATIFGLRXDRI = "ATIFGLRXDRI"; + private static final String X11_EXTENSION_ATIFGLEXTENSION = "ATIFGLEXTENSION"; /** * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties */ public static void initSingleton() { if(!isInit) { @@ -100,7 +106,7 @@ public class X11Util { if(!isInit) { isInit = true; if(DEBUG) { - System.out.println("X11UtilUtil.initSingleton()"); + System.out.println("X11Util.initSingleton()"); } if(!NWJNILibLoader.loadNativeWindow("x11")) { throw new NativeWindowException("NativeWindow X11 native library load error."); @@ -108,6 +114,7 @@ public class X11Util { final boolean isInitOK = initialize0( XERROR_STACKDUMP ); + final boolean hasX11_EXTENSION_ATIFGLRXDRI, hasX11_EXTENSION_ATIFGLEXTENSION; final long dpy = X11Lib.XOpenDisplay(null); if(0 != dpy) { if(XSYNC_ENABLED) { @@ -115,17 +122,29 @@ public class X11Util { } try { nullDisplayName = X11Lib.XDisplayString(dpy); + hasX11_EXTENSION_ATIFGLRXDRI = X11Lib.QueryExtension(dpy, X11_EXTENSION_ATIFGLRXDRI); + hasX11_EXTENSION_ATIFGLEXTENSION = X11Lib.QueryExtension(dpy, X11_EXTENSION_ATIFGLEXTENSION); } finally { X11Lib.XCloseDisplay(dpy); } } else { nullDisplayName = "nil"; + hasX11_EXTENSION_ATIFGLRXDRI = false; + hasX11_EXTENSION_ATIFGLEXTENSION = false; } + requiresGlobalToolkitLock = hasX11_EXTENSION_ATIFGLRXDRI || hasX11_EXTENSION_ATIFGLEXTENSION; + markAllDisplaysUnclosable = ATI_HAS_XCLOSEDISPLAY_BUG && ( hasX11_EXTENSION_ATIFGLRXDRI || hasX11_EXTENSION_ATIFGLEXTENSION ); if(DEBUG) { - System.err.println("X11Util init OK "+isInitOK+"]"+ - ", X11 Display(NULL) <"+nullDisplayName+">"+ - ", XSynchronize Enabled: "+XSYNC_ENABLED); + System.err.println("X11Util.initSingleton(): OK "+isInitOK+"]"+ + ",\n\t X11 Display(NULL) <"+nullDisplayName+">"+ + ",\n\t XSynchronize Enabled: " + XSYNC_ENABLED+ + ",\n\t X11_EXTENSION_ATIFGLRXDRI " + hasX11_EXTENSION_ATIFGLRXDRI+ + ",\n\t X11_EXTENSION_ATIFGLEXTENSION " + hasX11_EXTENSION_ATIFGLEXTENSION+ + ",\n\t requiresToolkitLock "+requiresToolkitLock()+ + ",\n\t requiresGlobalToolkitLock "+requiresGlobalToolkitLock()+ + ",\n\t markAllDisplaysUnclosable "+getMarkAllDisplaysUnclosable() + ); // Thread.dumpStack(); } } @@ -147,6 +166,7 @@ public class X11Util { * <p> * Called by {@link NativeWindowFactory#shutdown()} * </p> + * @see ToolkitProperties */ public static void shutdown() { if(isInit) { @@ -189,8 +209,20 @@ public class X11Util { } } - public static boolean requiresToolkitLock() { - return true; // JAWT locking: yes, instead of native X11 locking w use a recursive lock. + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean requiresToolkitLock() { + return true; // JAWT locking: yes, instead of native X11 locking w use a recursive lock per display connection. + } + + /** + * Called by {@link NativeWindowFactory#initSingleton()} + * @see ToolkitProperties + */ + public static final boolean requiresGlobalToolkitLock() { + return requiresGlobalToolkitLock; // JAWT locking: yes, instead of native X11 locking w use a global lock. } public static void setX11ErrorHandler(boolean onoff, boolean quiet) { @@ -206,9 +238,6 @@ public class X11Util { public static boolean getMarkAllDisplaysUnclosable() { return markAllDisplaysUnclosable; } - public static void setMarkAllDisplaysUnclosable(boolean v) { - markAllDisplaysUnclosable = v; - } private X11Util() {} @@ -373,7 +402,7 @@ public class X11Util { name = validateDisplayName(name); boolean reused = false; - synchronized(globalLock) { + synchronized(globalLock) { for(int i=0; i<reusableDisplayList.size(); i++) { if(reusableDisplayList.get(i).getName().equals(name)) { namedDpy = reusableDisplayList.remove(i); |