diff options
author | Sven Gothel <[email protected]> | 2011-12-11 04:47:27 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2011-12-11 04:47:27 +0100 |
commit | 9291731fec7103301ba36511ceb4375f63170e5c (patch) | |
tree | ec7f0d9c9ce611833840971206d2c591a94653b4 /src/jogl | |
parent | d9bbbbad1130bbe74856ecedb7cf1d2cc124b352 (diff) |
Fix concurrency bug of GLProfile initialization ; Fix SharedResourceRunner 'dead' thread (Applets)
GLDrawableFactory:
- clarify: public getWasSharedContextCreated(..) -> protected createSharedResource(..)
- add: getSharesResourceThread()
GLProfile:
- proper locking of initSingletion(..) path:
- Use RecursiveThreadGroupLock and add/remove GLDrawableFactory's sharesResourceThread
while creating it's the sharedResource.
This simplifies and fixes GLProfile's locking code.
- Fix and simplify initSingleton(boolean) API doc - mark it deprecated.
- Add initSingleton() for controlled initialization only, pairing w/ shutdown(..)
Remove initSingleton(boolean) calls in code and test!
+++
Fix SharedResourceRunner 'dead' thread (Applets)
In Applets, stopping an Applet makes the browser Java plugin interrupting
and killing all related threads, including our SharedResourceRunner thread.
- Validate whether the shared resource thread is alive
- Catch interruption in shared resource thread and assume it's a kill signal
- releaseSharedResource: clear devicesTried set
Diffstat (limited to 'src/jogl')
10 files changed, 296 insertions, 187 deletions
diff --git a/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java b/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java index 5fff1ce02..ff8d00ebe 100644 --- a/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java +++ b/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java @@ -89,7 +89,6 @@ import javax.media.opengl.GLProfile.ShutdownType; property <code>opengl.factory.class.name</code> to the fully-qualified name of the desired class. </P> */ - public abstract class GLDrawableFactory { private static final String nativeOSType; @@ -274,18 +273,31 @@ public abstract class GLDrawableFactory { } /** - * Returns true if a shared context could be created while initialization - * of shared resources for <code>device</code> {@link AbstractGraphicsDevice#getConnection()}.<br> - * This does not imply a shared context is mapped, but was available<br>. + * Validate and start the shared resource runner thread if necessary and + * if the implementation uses it. + * + * @return the shared resource runner thread, if implementation uses it. + */ + protected abstract Thread getSharedResourceThread(); + + /** + * Create the shared resource used internally as a reference for capabilities etc. + * <p> + * Returns true if a shared resource could be created + * for the <code>device</code> {@link AbstractGraphicsDevice#getConnection()}.<br> + * This does not imply a shared resource is mapped (ie. made persistent), but is available in general<br>. + * </p> * * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @return true if a shared resource could been created, otherwise false. */ - public abstract boolean getWasSharedContextCreated(AbstractGraphicsDevice device); - + protected abstract boolean createSharedResource(AbstractGraphicsDevice device); + /** * Returns the sole GLDrawableFactory instance for the desktop (X11, WGL, ..) if exist or null */ public static GLDrawableFactory getDesktopFactory() { + initSingleton(); return nativeOSFactory; } @@ -293,6 +305,7 @@ public abstract class GLDrawableFactory { * Returns the sole GLDrawableFactory instance for EGL if exist or null */ public static GLDrawableFactory getEGLFactory() { + initSingleton(); return eglFactory; } diff --git a/src/jogl/classes/javax/media/opengl/GLProfile.java b/src/jogl/classes/javax/media/opengl/GLProfile.java index 7f0c9b3d3..d52fbcd16 100644 --- a/src/jogl/classes/javax/media/opengl/GLProfile.java +++ b/src/jogl/classes/javax/media/opengl/GLProfile.java @@ -48,6 +48,8 @@ import com.jogamp.common.os.Platform; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.VersionUtil; import com.jogamp.common.util.cache.TempJarCache; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveThreadGroupLock; import com.jogamp.nativewindow.NativeWindowVersion; import com.jogamp.opengl.JoglVersion; @@ -80,65 +82,73 @@ public class GLProfile { } /** - * Static one time initialization of JOGL. + * Static initialization of JOGL. * <p> * The parameter <code>firstUIActionOnProcess</code> has an impact on concurrent locking,<br> * see {@link javax.media.nativewindow.NativeWindowFactory#initSingleton(boolean) NativeWindowFactory.initSingleton(firstUIActionOnProcess)}. * </p> * <p> - * Applications shall call this methods <b>ASAP</b>, before any other UI invocation.<br> - * You may issue the call in your <code>main class</code> static block, which is the earliest point in your application/applet lifecycle, - * or within the <code>main function</code>.<br> - * In case applications are able to initialize JOGL before any other UI action,<br> - * they shall invoke this method with <code>firstUIActionOnProcess=true</code> and benefit from fast native multithreading support on all platforms if possible.</P> + * Applications using this method may place it's call before any other UI invocation + * in the <code>main class</code>'s static block or within the <code>main function</code>. + * In such case, applications may pass <code>firstUIActionOnProcess=true</code> to use native toolkit locking.</P> * <P> - * RCP Application (Applet's, Webstart, Netbeans, ..) using JOGL may not be able to initialize JOGL - * before the first UI action.<br> - * In such case you shall invoke this method with <code>firstUIActionOnProcess=false</code>.<br> - * On some platforms, notably X11 with AWT usage, JOGL will utilize special locking mechanisms which may slow down your - * application.</P> + * RCP Application (Applet's, Webstart, Netbeans, ..) using JOGL are not be able to initialize JOGL + * before the first UI action. + * In such case you shall pass <code>firstUIActionOnProcess=false</code>.</P> * <P> - * Remark: NEWT is currently not affected by this behavior, ie always uses native multithreading.</P> - * <P> - * However, in case this method is not invoked, hence GLProfile is not initialized explicitly by the user,<br> - * the first call to {@link #getDefault()}, {@link #get(java.lang.String)}, etc, will initialize with <code>firstUIActionOnProcess=false</code>,<br> - * hence without the possibility to enable native multithreading.<br> - * This is not the recommended way, since it may has a performance impact, but it allows you to run code without explicit initialization.</P> + * In case this method is not invoked, GLProfile is initialized implicit by + * the first call to {@link #getDefault()}, {@link #get(java.lang.String)} passing <code>firstUIActionOnProcess=false</code>. * <P> * * @param firstUIActionOnProcess Should be <code>true</code> if called before the first UI action of the running program, * otherwise <code>false</code>. + * + * @deprecated This method shall not need to be called for other reasons than having a defined initialization sequence. + * To ensure homogeneous behavior with application not calling this method, you shall pass <code>firstUIActionOnProcess=false</code>. + * This method is subject to be removed in future versions of JOGL. */ public static void initSingleton(final boolean firstUIActionOnProcess) { - if(!initialized) { // volatile: ok - synchronized(GLProfile.class) { - if(!initialized) { - initialized = true; - if(DEBUG) { - System.err.println("GLProfile.initSingleton(firstUIActionOnProcess: "+firstUIActionOnProcess+") - thread "+Thread.currentThread().getName()); - Thread.dumpStack(); - } - Platform.initSingleton(); - - // run the whole static initialization privileged to speed up, - // since this skips checking further access - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - if(TempJarCache.isInitialized()) { - String[] atomicNativeJarBaseNames = new String[] { "nativewindow", "jogl", null }; - if( ReflectionUtil.isClassAvailable("com.jogamp.newt.NewtFactory", GLProfile.class.getClassLoader()) ) { - atomicNativeJarBaseNames[2] = "newt"; - } - JNILibLoaderBase.addNativeJarLibs(GLProfile.class, "jogl-all", atomicNativeJarBaseNames); - } - initProfilesForDefaultDevices(firstUIActionOnProcess); - return null; - } - }); + initLock.lock(); + try { + if(!initialized) { // volatile: ok + initialized = true; + if(DEBUG) { + System.err.println("GLProfile.initSingleton(firstUIActionOnProcess: "+firstUIActionOnProcess+") - thread "+Thread.currentThread().getName()); + Thread.dumpStack(); } + Platform.initSingleton(); + + // run the whole static initialization privileged to speed up, + // since this skips checking further access + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + if(TempJarCache.isInitialized()) { + String[] atomicNativeJarBaseNames = new String[] { "nativewindow", "jogl", null }; + if( ReflectionUtil.isClassAvailable("com.jogamp.newt.NewtFactory", GLProfile.class.getClassLoader()) ) { + atomicNativeJarBaseNames[2] = "newt"; + } + JNILibLoaderBase.addNativeJarLibs(GLProfile.class, "jogl-all", atomicNativeJarBaseNames); + } + initProfilesForDefaultDevices(firstUIActionOnProcess); + return null; + } + }); } + } finally { + initLock.unlock(); } } + + /** + * Static initialization of JOGL. + * + * <p> + * This method shall not need to be called for other reasons than having a defined initialization sequence. + * </p> + */ + public static void initSingleton() { + GLProfile.initSingleton(false); + } /** * Trigger eager initialization of GLProfiles for the given device, @@ -147,7 +157,7 @@ public class GLProfile { * @throws GLException if no profile for the given device is available. */ public static void initProfiles(AbstractGraphicsDevice device) throws GLException { - getProfileMap(device); + getProfileMap(device, true); } /** @@ -168,26 +178,29 @@ public class GLProfile { * Manual shutdown method, may be called after your last JOGL use * within the running JVM.<br> * It releases all temporary created resources, ie issues {@link javax.media.opengl.GLDrawableFactory#shutdown()}.<br> - * The shutdown implementation is called via the JVM shutdown hook, if not manually invoked here.<br> - * Invoke <code>shutdown(type)</code> manually is recommended, due to the unreliable JVM state within the shutdown hook.<br> + * The shutdown implementation is called via the JVM shutdown hook, if not manually invoked.<br> + * <p> + * This method shall not need to be called for other reasons than issuing a proper shutdown of resources. + * </p> * @param type the shutdown type, see {@link ShutdownType}. */ public static void shutdown(ShutdownType type) { - if(initialized) { // volatile: ok - synchronized(GLProfile.class) { - if(initialized) { - initialized = false; - if(DEBUG) { - System.err.println("GLProfile.shutdown(type: "+type+") - thread "+Thread.currentThread().getName()); - Thread.dumpStack(); - } - GLDrawableFactory.shutdown(type); - if(ShutdownType.COMPLETE == type) { - GLContext.shutdown(); - } - NativeWindowFactory.shutdown(); + initLock.lock(); + try { + if(initialized) { // volatile: ok + initialized = false; + if(DEBUG) { + System.err.println("GLProfile.shutdown(type: "+type+") - thread "+Thread.currentThread().getName()); + Thread.dumpStack(); + } + GLDrawableFactory.shutdown(type); + if(ShutdownType.COMPLETE == type) { + GLContext.shutdown(); } + NativeWindowFactory.shutdown(); } + } finally { + initLock.unlock(); } } @@ -204,12 +217,13 @@ public class GLProfile { * @return true if the profile is available for the device, otherwise false. */ public static boolean isAvailable(AbstractGraphicsDevice device, String profile) { - try { - return null != getProfileMap(device).get(profile); - } catch (GLException gle) { /* profiles for device n/a */ } - return false; + initSingleton(); + return isAvailableImpl(getProfileMap(device, false), profile); } - + private static boolean isAvailableImpl(HashMap<String /*GLProfile_name*/, GLProfile> map, String profile) { + return null != map && null != map.get(profile); + } + /** * Returns the availability of a profile on the default device. * @@ -234,74 +248,71 @@ public class GLProfile { boolean avail; StringBuffer sb = new StringBuffer(); - validateInitialization(); - + initSingleton(); + if(null==device) { device = defaultDevice; } - + final HashMap<String /*GLProfile_name*/, GLProfile> map = getProfileMap(device, false); + sb.append("GLAvailability[Native[GL4bc "); - avail=isAvailable(device, GL4bc); + avail=isAvailableImpl(map, GL4bc); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 4, GLContext.CTX_PROFILE_COMPAT); } sb.append(", GL4 "); - avail=isAvailable(device, GL4); + avail=isAvailableImpl(map, GL4); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 4, GLContext.CTX_PROFILE_CORE); } sb.append(", GL3bc "); - avail=isAvailable(device, GL3bc); + avail=isAvailableImpl(map, GL3bc); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 3, GLContext.CTX_PROFILE_COMPAT); } sb.append(", GL3 "); - avail=isAvailable(device, GL3); + avail=isAvailableImpl(map, GL3); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 3, GLContext.CTX_PROFILE_CORE); } sb.append(", GL2 "); - avail=isAvailable(device, GL2); + avail=isAvailableImpl(map, GL2); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 2, GLContext.CTX_PROFILE_COMPAT); } sb.append(", GL2ES1 "); - sb.append(isAvailable(device, GL2ES1)); + sb.append(isAvailableImpl(map, GL2ES1)); sb.append(", GLES1 "); - avail=isAvailable(device, GLES1); + avail=isAvailableImpl(map, GLES1); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 1, GLContext.CTX_PROFILE_ES); } sb.append(", GL2ES2 "); - sb.append(isAvailable(device, GL2ES2)); + sb.append(isAvailableImpl(map, GL2ES2)); sb.append(", GLES2 "); - avail=isAvailable(device, GLES2); + avail=isAvailableImpl(map, GLES2); sb.append(avail); if(avail) { glAvailabilityToString(device, sb, 2, GLContext.CTX_PROFILE_ES); } sb.append("], Profiles["); - HashMap<String /*GLProfile_name*/, GLProfile> profileMap = null; - try { - profileMap = getProfileMap(device); - } catch (GLException gle) { /* profiles for device n/a */ } - if(null != profileMap) { - for(Iterator<GLProfile> i=profileMap.values().iterator(); i.hasNext(); ) { + if(null != map) { + for(Iterator<GLProfile> i=map.values().iterator(); i.hasNext(); ) { sb.append(i.next().toString()); sb.append(", "); } @@ -316,7 +327,7 @@ public class GLProfile { return sb.toString(); } - + /** Uses the default device */ public static String glAvailabilityToString() { return glAvailabilityToString(null); @@ -637,7 +648,7 @@ public class GLProfile { if(null==profile || profile.equals("GL")) { profile = GL_DEFAULT; } - final HashMap<String /*GLProfile_name*/, GLProfile> glpMap = getProfileMap(device); + final HashMap<String /*GLProfile_name*/, GLProfile> glpMap = getProfileMap(device, true); final GLProfile glp = glpMap.get(profile); if(null == glp) { throw new GLException("Profile "+profile+" is not available on "+device+", but: "+glpMap.values()); @@ -667,7 +678,7 @@ public class GLProfile { public static GLProfile get(AbstractGraphicsDevice device, String[] profiles) throws GLException { - HashMap<String /*GLProfile_name*/, GLProfile> map = getProfileMap(device); + HashMap<String /*GLProfile_name*/, GLProfile> map = getProfileMap(device, true); for(int i=0; i<profiles.length; i++) { String profile = profiles[i]; GLProfile glProfile = map.get(profile); @@ -1193,6 +1204,7 @@ public class GLProfile { private static /*final*/ AbstractGraphicsDevice defaultEGLDevice; private static volatile boolean initialized = false; + private static RecursiveThreadGroupLock initLock = LockFactory.createRecursiveThreadGroupLock(); /** * Tries the profiles implementation and native libraries. @@ -1332,19 +1344,24 @@ public class GLProfile { * @param device the device for which profiles shall be initialized * @return true if any profile for the device exists, otherwise false */ - private static synchronized boolean initProfilesForDevice(AbstractGraphicsDevice device) { + private static boolean initProfilesForDevice(AbstractGraphicsDevice device) { if(null == device) { return false; } - GLDrawableFactory factory = GLDrawableFactory.getFactoryImpl(device); - factory.enterThreadCriticalZone(); + initLock.lock(); try { - return initProfilesForDeviceCritical(device); + GLDrawableFactory factory = GLDrawableFactory.getFactoryImpl(device); + factory.enterThreadCriticalZone(); + try { + return initProfilesForDeviceCritical(device); + } finally { + factory.leaveThreadCriticalZone(); + } } finally { - factory.leaveThreadCriticalZone(); + initLock.unlock(); } } - private static synchronized boolean initProfilesForDeviceCritical(AbstractGraphicsDevice device) { + private static boolean initProfilesForDeviceCritical(AbstractGraphicsDevice device) { boolean isSet = GLContext.getAvailableGLVersionsSet(device); if(DEBUG) { @@ -1368,7 +1385,14 @@ public class GLProfile { // Triggers eager initialization of share context in GLDrawableFactory for the device, // hence querying all available GLProfiles - boolean desktopSharedCtxAvail = desktopFactory.getWasSharedContextCreated(device); + final Thread sharedResourceThread = desktopFactory.getSharedResourceThread(); + if(null != sharedResourceThread) { + initLock.addOwner(sharedResourceThread); + } + boolean desktopSharedCtxAvail = desktopFactory.createSharedResource(device); + if(null != sharedResourceThread) { + initLock.removeOwner(sharedResourceThread); + } if(!desktopSharedCtxAvail) { hasDesktopGLFactory = false; } @@ -1390,7 +1414,14 @@ public class GLProfile { // Triggers eager initialization of share context in GLDrawableFactory for the device, // hence querying all available GLProfiles - boolean eglSharedCtxAvail = eglFactory.getWasSharedContextCreated(device); + final Thread sharedResourceThread = eglFactory.getSharedResourceThread(); + if(null != sharedResourceThread) { + initLock.addOwner(sharedResourceThread); + } + boolean eglSharedCtxAvail = eglFactory.createSharedResource(device); + if(null != sharedResourceThread) { + initLock.removeOwner(sharedResourceThread); + } if(!eglSharedCtxAvail) { // Remark: On Windows there is a libEGL.dll delivered w/ Chrome 15.0.874.121m and Firefox 8.0.1 // but it seems even EGL.eglInitialize(eglDisplay, null, null) @@ -1471,30 +1502,20 @@ public class GLProfile { } public static AbstractGraphicsDevice getDefaultDevice() { - validateInitialization(); + initSingleton(); return defaultDevice; } public static AbstractGraphicsDevice getDefaultDesktopDevice() { - validateInitialization(); + initSingleton(); return defaultDesktopDevice; } public static AbstractGraphicsDevice getDefaultEGLDevice() { - validateInitialization(); + initSingleton(); return defaultEGLDevice; } - private static void validateInitialization() { - if(!initialized) { // volatile: ok - synchronized(GLProfile.class) { - if(!initialized) { - initSingleton(false); - } - } - } - } - private static String array2String(String[] list) { StringBuffer msg = new StringBuffer(); msg.append("["); @@ -1656,29 +1677,38 @@ public class GLProfile { * - initialization<br< * * @param device the key 'device -> GLProfiles-Map' + * @param throwExceptionOnZeroProfile true if <code>GLException</code> shall be thrown in case of no mapped profile, otherwise false. * @return the GLProfile HashMap if exists, otherwise null * @throws GLException if no profile for the given device is available. */ - private static HashMap<String /*GLProfile_name*/, GLProfile> getProfileMap(AbstractGraphicsDevice device) throws GLException { - validateInitialization(); + private static HashMap<String /*GLProfile_name*/, GLProfile> getProfileMap(AbstractGraphicsDevice device, boolean throwExceptionOnZeroProfile) + throws GLException + { + initSingleton(); + if(null==device) { device = defaultDevice; } String deviceKey = device.getUniqueID(); HashMap<String /*GLProfile_name*/, GLProfile> map = deviceConn2ProfileMap.get(deviceKey); - if( null == map ) { - if( !initProfilesForDevice(device) ) { + if( null != map ) { + return map; + } + if( !initProfilesForDevice(device) ) { + if( throwExceptionOnZeroProfile ) { throw new GLException("No Profile available for "+device); + } else { + return null; } - if( null == deviceConn2ProfileMap.get(deviceKey) ) { - throw new InternalError("initProfilesForDevice(..) didn't issue setProfileMap(..) on "+device); - } + } + map = deviceConn2ProfileMap.get(deviceKey); + if( null == map && throwExceptionOnZeroProfile ) { + throw new InternalError("initProfilesForDevice(..) didn't setProfileMap(..) for "+device); } return map; } private static void setProfileMap(AbstractGraphicsDevice device, HashMap<String /*GLProfile_name*/, GLProfile> mappedProfiles) { - validateInitialization(); synchronized ( deviceConn2ProfileMap ) { deviceConn2ProfileMap.put(device.getUniqueID(), mappedProfiles); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index 329bb125e..ab4276dee 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -1047,7 +1047,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // System.err.println(NativeWindowVersion.getInstance()); System.err.println(JoglVersion.getInstance()); - GLProfile.initSingleton(false); GLDrawableFactory factory = GLDrawableFactory.getDesktopFactory(); List<GLCapabilitiesImmutable> availCaps = factory.getAvailableCapabilities(null); for(int i=0; i<availCaps.size(); i++) { diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index 5ea1a9bac..0d372587e 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -459,8 +459,6 @@ public abstract class GLContextImpl extends GLContext { throw new GLException("drawable has invalid handle: "+drawable); } if (!isCreated()) { - GLProfile.initProfiles( - getGLDrawable().getNativeSurface().getGraphicsConfiguration().getScreen().getDevice()); final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getShareContext(this); if (null != shareWith) { shareWith.getDrawableImpl().lockSurface(); diff --git a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java index e24a9f8ed..a33e03a1a 100644 --- a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java +++ b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java @@ -44,6 +44,10 @@ public class SharedResourceRunner implements Runnable { } public static interface Implementation { + /** + * @param connection for creation a {@link AbstractGraphicsDevice} instance. + * @return A new shared resource instance + */ Resource createSharedResource(String connection); void releaseSharedResource(Resource shared); void clear(); @@ -53,15 +57,15 @@ public class SharedResourceRunner implements Runnable { Collection<Resource> mapValues(); } - Implementation impl = null; + final HashSet<String> devicesTried = new HashSet<String>(); + final Implementation impl; - boolean ready = false; - boolean released = false; - boolean shouldRelease = false; - String initConnection = null; - String releaseConnection = null; - - HashSet<String> devicesTried = new HashSet<String>(); + Thread thread; + boolean ready; + boolean released; + boolean shouldRelease; + String initConnection; + String releaseConnection; private boolean getDeviceTried(String connection) { synchronized (devicesTried) { @@ -81,22 +85,82 @@ public class SharedResourceRunner implements Runnable { public SharedResourceRunner(Implementation impl) { this.impl = impl; + resetState(); + } + + private void resetState() { + devicesTried.clear(); + thread = null; + ready = false; + released = false; + shouldRelease = false; + initConnection = null; + releaseConnection = null; } + /** + * Start the shared resource runner thread, if not running. + * <p> + * Validate the thread upfront and release all related resource if it was killed. + * </p> + * + * @return the shared resource runner thread. + */ + public Thread start() { + if(null != thread && !thread.isAlive()) { + // thread was killed unrecognized .. + if (DEBUG) { + System.err.println("SharedResourceRunner.start() - dead-old-thread cleanup - "+Thread.currentThread().getName()); + } + releaseSharedResources(); + thread = null; + } + if(null == thread) { + if (DEBUG) { + System.err.println("SharedResourceRunner.start() - start new Thread - "+Thread.currentThread().getName()); + } + resetState(); + thread = new Thread(this, Thread.currentThread().getName()+"-SharedResourceRunner"); + thread.setDaemon(true); // Allow JVM to exit, even if this one is running + thread.start(); + } + return thread; + } + + public void stop() { + if(null != thread) { + if (DEBUG) { + System.err.println("SharedResourceRunner.stop() - "+Thread.currentThread().getName()); + } + synchronized (this) { + shouldRelease = true; + this.notifyAll(); + + while (!released) { + try { + this.wait(); + } catch (InterruptedException ex) { + } + } + } + } + } + public SharedResourceRunner.Resource getOrCreateShared(AbstractGraphicsDevice device) { SharedResourceRunner.Resource sr = null; if(null != device) { + start(); final String connection = device.getConnection(); sr = impl.mapGet(connection); if (null == sr && !getDeviceTried(connection)) { addDeviceTried(connection); if (DEBUG) { - System.err.println("getOrCreateShared() " + connection + ": trying"); + System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": trying - "+Thread.currentThread().getName()); } doAndWait(connection, null); sr = impl.mapGet(connection); if (DEBUG) { - System.err.println("getOrCreateShared() " + connection + ": "+ ( ( null != sr ) ? "success" : "failed" ) ); + System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": "+ ( ( null != sr ) ? "success" : "failed" ) +" - "+Thread.currentThread().getName()); } } } @@ -111,11 +175,11 @@ public class SharedResourceRunner implements Runnable { if (null != sr) { removeDeviceTried(connection); if (DEBUG) { - System.err.println("releaseShared() " + connection + ": trying"); + System.err.println("SharedResourceRunner.releaseShared() " + connection + ": trying - "+Thread.currentThread().getName()); } doAndWait(null, connection); if (DEBUG) { - System.err.println("releaseShared() " + connection + ": done"); + System.err.println("SharedResourceRunner.releaseShared() " + connection + ": done - "+Thread.currentThread().getName()); } } } @@ -125,9 +189,9 @@ public class SharedResourceRunner implements Runnable { private final void doAndWait(String initConnection, String releaseConnection) { // wait until thread becomes ready to init new device, // pass the device and release the sync - String threadName = Thread.currentThread().getName(); + final String threadName = Thread.currentThread().getName(); if (DEBUG) { - System.err.println(threadName + " doAndWait START init: " + initConnection + ", release: "+releaseConnection); + System.err.println("SharedResourceRunner.doAndWait() START init: " + initConnection + ", release: "+releaseConnection+" - "+threadName); } synchronized (this) { while (!ready) { @@ -137,7 +201,7 @@ public class SharedResourceRunner implements Runnable { } } if (DEBUG) { - System.err.println(threadName + " initializeAndWait set command init: " + initConnection + ", release: "+releaseConnection); + System.err.println("SharedResourceRunner.doAndWait() set command: " + initConnection + ", release: "+releaseConnection+" - "+threadName); } this.initConnection = initConnection; this.releaseConnection = releaseConnection; @@ -151,31 +215,17 @@ public class SharedResourceRunner implements Runnable { } } if (DEBUG) { - System.err.println(threadName + " initializeAndWait END init: " + initConnection + ", release: "+releaseConnection); + System.err.println("SharedResourceRunner.initializeAndWait END init: " + initConnection + ", release: "+releaseConnection+" - "+threadName); } } // done } - public final void releaseAndWait() { - synchronized (this) { - shouldRelease = true; - this.notifyAll(); - - while (!released) { - try { - this.wait(); - } catch (InterruptedException ex) { - } - } - } - } - public final void run() { - String threadName = Thread.currentThread().getName(); + final String threadName = Thread.currentThread().getName(); if (DEBUG) { - System.err.println(threadName + " STARTED"); + System.err.println("SharedResourceRunner.run(): STARTED - " + threadName); } synchronized (this) { @@ -184,21 +234,27 @@ public class SharedResourceRunner implements Runnable { // wait for stop or init ready = true; if (DEBUG) { - System.err.println(threadName + " -> ready"); + System.err.println("SharedResourceRunner.run(): READY - " + threadName); } notifyAll(); this.wait(); - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + shouldRelease = true; + if(DEBUG) { + System.err.println("SharedResourceRunner.run(): INTERRUPTED - "+Thread.currentThread().getName()); + ex.printStackTrace(); + } + } ready = false; if (!shouldRelease) { if (DEBUG) { - System.err.println(threadName + " woke up for device connection init: " + initConnection + - ", release: " + releaseConnection); + System.err.println("SharedResourceRunner.run(): WOKE UP for device connection init: " + initConnection + + ", release: " + releaseConnection + " - " + threadName); } if(null != initConnection) { if (DEBUG) { - System.err.println(threadName + " create Shared for: " + initConnection); + System.err.println("SharedResourceRunner.run(): create Shared for: " + initConnection + " - " + threadName); } Resource sr = null; try { @@ -212,7 +268,7 @@ public class SharedResourceRunner implements Runnable { } if(null != releaseConnection) { if (DEBUG) { - System.err.println(threadName + " release Shared for: " + releaseConnection); + System.err.println("SharedResourceRunner.run(): release Shared for: " + releaseConnection + " - " + threadName); } Resource sr = impl.mapGet(releaseConnection); if (null != sr) { @@ -230,25 +286,34 @@ public class SharedResourceRunner implements Runnable { } if (DEBUG) { - System.err.println(threadName + " release START"); + System.err.println("SharedResourceRunner.run(): RELEASE START - " + threadName); } releaseSharedResources(); if (DEBUG) { - System.err.println(threadName + " release END"); + System.err.println("SharedResourceRunner.run(): RELEASE END - " + threadName); } + shouldRelease = false; released = true; - ready = false; + thread = null; notifyAll(); } } private void releaseSharedResources() { + synchronized (devicesTried) { + devicesTried.clear(); + } Collection<Resource> sharedResources = impl.mapValues(); for (Iterator<Resource> iter = sharedResources.iterator(); iter.hasNext();) { - impl.releaseSharedResource(iter.next()); + try { + impl.releaseSharedResource(iter.next()); + } catch (Throwable t) { + System.err.println("Catched Exception: "+t.getStackTrace()+" - "+Thread.currentThread().getName()); + t.printStackTrace(); + } } impl.clear(); } diff --git a/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java b/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java index b7c90a18b..55fb3f9a2 100644 --- a/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java +++ b/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java @@ -131,7 +131,6 @@ public class VersionApplet extends Applet { public void init() { System.err.println("VersionApplet: init() - begin"); - GLProfile.initSingleton(false); my_init(); System.err.println("VersionApplet: init() - end"); } @@ -151,7 +150,6 @@ public class VersionApplet extends Applet { public void destroy() { System.err.println("VersionApplet: destroy() - start"); my_release(); - GLProfile.shutdown(GLProfile.ShutdownType.SHARED_ONLY); System.err.println("VersionApplet: destroy() - end"); } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java index f6988a73f..cd6d61a22 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java @@ -232,7 +232,11 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { return sr; } - public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) { + protected final Thread getSharedResourceThread() { + return null; + } + + protected final boolean createSharedResource(AbstractGraphicsDevice device) { try { SharedResource sr = getOrCreateEGLSharedResource(device); if(null!=sr) { diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java index 45445067e..ed53a9ee5 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java @@ -252,8 +252,12 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { } return sr; } - - public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) { + + protected final Thread getSharedResourceThread() { + return null; + } + + protected final boolean createSharedResource(AbstractGraphicsDevice device) { try { SharedResource sr = getOrCreateOSXSharedResource(device); if(null!=sr) { diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java index 917402b0a..43c16240d 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java @@ -119,17 +119,14 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { // Init shared resources off thread // Will be released via ShutdownHook - sharedResourceImpl = new SharedResourceImplementation(); - sharedResourceRunner = new SharedResourceRunner(sharedResourceImpl); - sharedResourceThread = new Thread(sharedResourceRunner, Thread.currentThread().getName()+"-SharedResourceRunner"); - sharedResourceThread.setDaemon(true); // Allow JVM to exit, even if this one is running - sharedResourceThread.start(); + sharedResourceRunner = new SharedResourceRunner(new SharedResourceImplementation()); + sharedResourceRunner.start(); } } protected final void destroy(ShutdownType shutdownType) { if(null != sharedResourceRunner) { - sharedResourceRunner.releaseAndWait(); + sharedResourceRunner.stop(); sharedResourceRunner = null; } if(null != sharedMap) { @@ -153,9 +150,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } private WindowsGraphicsDevice defaultDevice; - private SharedResourceImplementation sharedResourceImpl; private SharedResourceRunner sharedResourceRunner; - private Thread sharedResourceThread; private HashMap<String /*connection*/, SharedResourceRunner.Resource> sharedMap; private long processAffinityChanges = 0; @@ -397,7 +392,11 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { final static String WGL_ARB_make_current_read = "WGL_ARB_make_current_read"; final static String wglMakeContextCurrent = "wglMakeContextCurrent"; - public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) { + protected final Thread getSharedResourceThread() { + return sharedResourceRunner.start(); + } + + protected final boolean createSharedResource(AbstractGraphicsDevice device) { try { SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); if(null!=sr) { diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java index 96153dd27..235aea499 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java @@ -111,17 +111,14 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { // Init shared resources off thread // Will be released via ShutdownHook - sharedResourceImpl = new SharedResourceImplementation(); - sharedResourceRunner = new SharedResourceRunner(sharedResourceImpl); - sharedResourceThread = new Thread(sharedResourceRunner, Thread.currentThread().getName()+"-SharedResourceRunner"); - sharedResourceThread.setDaemon(true); // Allow JVM to exit, even if this one is running - sharedResourceThread.start(); + sharedResourceRunner = new SharedResourceRunner(new SharedResourceImplementation()); + sharedResourceRunner.start(); } } protected final void destroy(ShutdownType shutdownType) { if(null != sharedResourceRunner) { - sharedResourceRunner.releaseAndWait(); + sharedResourceRunner.stop(); sharedResourceRunner = null; } if(null != sharedMap) { @@ -147,9 +144,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } private X11GraphicsDevice defaultDevice; - private SharedResourceImplementation sharedResourceImpl; private SharedResourceRunner sharedResourceRunner; - private Thread sharedResourceThread; private HashMap<String /* connection */, SharedResourceRunner.Resource> sharedMap; static class SharedResource implements SharedResourceRunner.Resource { @@ -322,7 +317,11 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { return false; } - public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) { + protected final Thread getSharedResourceThread() { + return sharedResourceRunner.start(); + } + + protected final boolean createSharedResource(AbstractGraphicsDevice device) { try { SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); if(null!=sr) { |