From 9291731fec7103301ba36511ceb4375f63170e5c Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sun, 11 Dec 2011 04:47:27 +0100 Subject: 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 --- make/scripts/tests.sh | 3 +- .../javax/media/opengl/GLDrawableFactory.java | 25 +- src/jogl/classes/javax/media/opengl/GLProfile.java | 256 ++++++++++++--------- .../classes/javax/media/opengl/awt/GLCanvas.java | 1 - src/jogl/classes/jogamp/opengl/GLContextImpl.java | 2 - .../jogamp/opengl/SharedResourceRunner.java | 149 ++++++++---- .../classes/jogamp/opengl/awt/VersionApplet.java | 2 - .../jogamp/opengl/egl/EGLDrawableFactory.java | 6 +- .../macosx/cgl/MacOSXCGLDrawableFactory.java | 8 +- .../windows/wgl/WindowsWGLDrawableFactory.java | 17 +- .../opengl/x11/glx/X11GLXDrawableFactory.java | 17 +- .../jogamp/newt/awt/applet/JOGLNewtApplet1Run.java | 2 - .../jogamp/newt/awt/applet/JOGLNewtAppletBase.java | 2 + .../newt/driver/android/NewtBaseActivity.java | 5 - .../test/junit/graph/TestRegionRendererNEWT01.java | 5 - .../test/junit/graph/TestTextRendererNEWT01.java | 5 - .../junit/graph/demos/GPURegionNewtDemo01.java | 1 - .../junit/graph/demos/GPURegionNewtDemo02.java | 1 - .../test/junit/graph/demos/GPUTextNewtDemo01.java | 1 - .../test/junit/graph/demos/GPUTextNewtDemo02.java | 1 - .../junit/graph/demos/GPUUISceneNewtDemo01.java | 1 - .../junit/graph/demos/GPUUISceneNewtDemo02.java | 1 - .../test/junit/graph/demos/ui/UINewtDemo01.java | 1 - .../test/junit/jogl/acore/TestGLDebug00NEWT.java | 5 - .../test/junit/jogl/acore/TestGLDebug01NEWT.java | 6 - .../junit/jogl/acore/TestInitConcurrentNEWT.java | 194 ++++++++++++++++ .../test/junit/jogl/acore/TestNVSwapGroupNEWT.java | 1 - .../jogl/acore/TestSharedContextListNEWT.java | 1 - .../jogl/acore/TestSharedContextListNEWT2.java | 1 - .../jogl/acore/TestSharedContextVBOES1NEWT.java | 1 - .../jogl/acore/TestSharedContextVBOES2NEWT.java | 1 - .../jogl/acore/TestSharedContextVBOES2NEWT2.java | 1 - .../junit/jogl/acore/TestShutdownCompleteAWT.java | 2 +- .../junit/jogl/acore/TestShutdownCompleteNEWT.java | 2 +- .../junit/jogl/acore/TestShutdownSharedAWT.java | 2 +- .../junit/jogl/acore/TestShutdownSharedNEWT.java | 2 +- .../test/junit/jogl/caps/TestTranslucencyNEWT.java | 1 - .../jogl/demos/es1/newt/TestGearsES1NEWT.java | 1 - .../jogl/demos/es1/newt/TestRedSquareES1NEWT.java | 1 - .../es2/newt/TestElektronenMultipliziererNEWT.java | 5 - .../jogl/demos/es2/newt/TestGearsES2NEWT.java | 1 - .../jogl/demos/es2/newt/TestRedSquareES2NEWT.java | 1 - .../junit/jogl/demos/gl2/awt/TestGearsAWT.java | 1 - .../junit/jogl/demos/gl2/newt/TestGearsNEWT.java | 1 - .../junit/jogl/drawable/TestDrawable01NEWT.java | 1 - .../test/junit/jogl/glsl/TestFBOMRTNEWT01.java | 5 - .../junit/jogl/glsl/TestGLSLShaderState01NEWT.java | 5 - .../junit/jogl/glsl/TestGLSLShaderState02NEWT.java | 5 - .../test/junit/jogl/glsl/TestGLSLSimple01NEWT.java | 6 - .../test/junit/jogl/glsl/TestRulerNEWT01.java | 5 - .../TestTransformFeedbackVaryingsBug407NEWT.java | 5 - ...TestSwingAWTRobotUsageBeforeJOGLInitBug411.java | 6 +- .../offscreen/TestOffscreen01GLPBufferNEWT.java | 1 - .../jogl/offscreen/TestOffscreen02BitmapNEWT.java | 1 - .../opengl/test/junit/jogl/swt/TestSWT01GLn.java | 1 - .../opengl/test/junit/jogl/swt/TestSWT02GLn.java | 1 - .../junit/newt/TestDisplayLifecycle01NEWT.java | 1 - .../junit/newt/TestDisplayLifecycle02NEWT.java | 1 - .../test/junit/newt/TestGLWindows00NEWT.java | 2 - .../test/junit/newt/TestGLWindows01NEWT.java | 2 - .../junit/newt/TestGLWindows02NEWTAnimated.java | 1 - .../test/junit/newt/TestRemoteGLWindows01NEWT.java | 10 +- .../test/junit/newt/TestScreenMode01NEWT.java | 3 +- .../test/junit/newt/TestScreenMode01bNEWT.java | 1 - .../test/junit/newt/TestScreenMode02NEWT.java | 1 - .../junit/newt/parenting/TestParenting01NEWT.java | 1 - .../junit/newt/parenting/TestParenting02NEWT.java | 1 - .../junit/newt/parenting/TestParenting03AWT.java | 4 - 68 files changed, 501 insertions(+), 312 deletions(-) create mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrentNEWT.java diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index a465fe4fe..dc9392e0a 100755 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -172,6 +172,7 @@ function testswt() { #testawt com.jogamp.opengl.test.junit.jogl.acore.TestShutdownSharedAWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestShutdownCompleteNEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestShutdownSharedNEWT $* +testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestInitConcurrentNEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLProfile01NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLDebug00NEWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestGLDebug01NEWT $* @@ -251,7 +252,7 @@ function testswt() { #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParenting03AWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParentingFocusTraversal01AWT $* #testawt com.jogamp.opengl.test.junit.newt.parenting.TestParentingOffscreenLayer01AWT $* -testawt com.jogamp.opengl.test.junit.newt.parenting.TestTranslucentParentingAWT $* +#testawt com.jogamp.opengl.test.junit.newt.parenting.TestTranslucentParentingAWT $* #testawt com.jogamp.opengl.test.junit.newt.TestCloseNewtAWT #testawt com.jogamp.opengl.test.junit.jogl.caps.TestMultisampleAWT $* #testawt com.jogamp.opengl.test.junit.jogl.caps.TestMultisampleNEWT $* 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 opengl.factory.class.name to the fully-qualified name of the desired class.

*/ - 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 device {@link AbstractGraphicsDevice#getConnection()}.
- * This does not imply a shared context is mapped, but was available
. + * 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. + *

+ * Returns true if a shared resource could be created + * for the device {@link AbstractGraphicsDevice#getConnection()}.
+ * This does not imply a shared resource is mapped (ie. made persistent), but is available in general
. + *

* * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be null 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. *

* The parameter firstUIActionOnProcess has an impact on concurrent locking,
* see {@link javax.media.nativewindow.NativeWindowFactory#initSingleton(boolean) NativeWindowFactory.initSingleton(firstUIActionOnProcess)}. *

*

- * Applications shall call this methods ASAP, before any other UI invocation.
- * You may issue the call in your main class static block, which is the earliest point in your application/applet lifecycle, - * or within the main function.
- * In case applications are able to initialize JOGL before any other UI action,
- * they shall invoke this method with firstUIActionOnProcess=true and benefit from fast native multithreading support on all platforms if possible.

+ * Applications using this method may place it's call before any other UI invocation + * in the main class's static block or within the main function. + * In such case, applications may pass firstUIActionOnProcess=true to use native toolkit locking.

*

- * RCP Application (Applet's, Webstart, Netbeans, ..) using JOGL may not be able to initialize JOGL - * before the first UI action.
- * In such case you shall invoke this method with firstUIActionOnProcess=false.
- * On some platforms, notably X11 with AWT usage, JOGL will utilize special locking mechanisms which may slow down your - * application.

+ * 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 firstUIActionOnProcess=false.

*

- * Remark: NEWT is currently not affected by this behavior, ie always uses native multithreading.

- *

- * However, in case this method is not invoked, hence GLProfile is not initialized explicitly by the user,
- * the first call to {@link #getDefault()}, {@link #get(java.lang.String)}, etc, will initialize with firstUIActionOnProcess=false,
- * hence without the possibility to enable native multithreading.
- * This is not the recommended way, since it may has a performance impact, but it allows you to run code without explicit initialization.

+ * In case this method is not invoked, GLProfile is initialized implicit by + * the first call to {@link #getDefault()}, {@link #get(java.lang.String)} passing firstUIActionOnProcess=false. *

* * @param firstUIActionOnProcess Should be true if called before the first UI action of the running program, * otherwise false. + * + * @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 firstUIActionOnProcess=false. + * 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() { - 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() { + 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. + * + *

+ * This method shall not need to be called for other reasons than having a defined initialization sequence. + *

+ */ + 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.
* It releases all temporary created resources, ie issues {@link javax.media.opengl.GLDrawableFactory#shutdown()}.
- * The shutdown implementation is called via the JVM shutdown hook, if not manually invoked here.
- * Invoke shutdown(type) manually is recommended, due to the unreliable JVM state within the shutdown hook.
+ * The shutdown implementation is called via the JVM shutdown hook, if not manually invoked.
+ *

+ * This method shall not need to be called for other reasons than issuing a proper shutdown of resources. + *

* @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 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 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 profileMap = null; - try { - profileMap = getProfileMap(device); - } catch (GLException gle) { /* profiles for device n/a */ } - if(null != profileMap) { - for(Iterator i=profileMap.values().iterator(); i.hasNext(); ) { + if(null != map) { + for(Iterator 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 glpMap = getProfileMap(device); + final HashMap 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 map = getProfileMap(device); + HashMap map = getProfileMap(device, true); for(int i=0; i GLProfiles-Map' + * @param throwExceptionOnZeroProfile true if GLException 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 getProfileMap(AbstractGraphicsDevice device) throws GLException { - validateInitialization(); + private static HashMap getProfileMap(AbstractGraphicsDevice device, boolean throwExceptionOnZeroProfile) + throws GLException + { + initSingleton(); + if(null==device) { device = defaultDevice; } String deviceKey = device.getUniqueID(); HashMap 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 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 availCaps = factory.getAvailableCapabilities(null); for(int i=0; i mapValues(); } - Implementation impl = null; + final HashSet devicesTried = new HashSet(); + final Implementation impl; - boolean ready = false; - boolean released = false; - boolean shouldRelease = false; - String initConnection = null; - String releaseConnection = null; - - HashSet devicesTried = new HashSet(); + 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. + *

+ * Validate the thread upfront and release all related resource if it was killed. + *

+ * + * @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 sharedResources = impl.mapValues(); for (Iterator 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 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 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) { diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java index ebcc0dd27..105f8369a 100755 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java +++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtApplet1Run.java @@ -170,7 +170,6 @@ public class JOGLNewtApplet1Run extends Applet { glTrace); try { - GLProfile.initSingleton(false); GLCapabilities caps = new GLCapabilities(GLProfile.get(glProfileName)); caps.setAlphaBits(glAlphaBits); if(0=0; i--) { + if(!tasks[i].done()) { + return false; + } + } + return true; + } + protected static String doneDump(JOGLTask[] tasks) { + StringBuffer sb = new StringBuffer(); + sb.append("["); + for(int i=0; i0) { + sb.append(", "); + } + sb.append(i).append(": ").append(tasks[i].done()); + } + sb.append("]"); + return sb.toString(); + } + + protected static boolean isDead(Thread[] threads) { + for(int i=threads.length-1; i>=0; i--) { + if(threads[i].isAlive()) { + return false; + } + } + return true; + } + protected static String isAliveDump(Thread[] threads) { + StringBuffer sb = new StringBuffer(); + sb.append("["); + for(int i=0; i0) { + sb.append(", "); + } + sb.append(i).append(": ").append(threads[i].isAlive()); + } + sb.append("]"); + return sb.toString(); + } + + protected void runJOGLTasks(int num) throws InterruptedException { + final String currentThreadName = Thread.currentThread().getName(); + final Object sync = new Object(); + final JOGLTask[] tasks = new JOGLTask[num]; + final Thread[] threads = new Thread[num]; + int i; + for(i=0; i