diff options
author | Sven Gothel <[email protected]> | 2014-07-31 02:42:39 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-07-31 02:42:39 +0200 |
commit | ba1ffe66697c3175b423cb7ab9b686d73959708d (patch) | |
tree | 13d4daed94906e96f0001cc9df5ad4d6c8cd3518 /src/jogl/classes | |
parent | 2e1484f4c25605c6dc0307f12f3e4e434c429a37 (diff) |
Bug 1039 - Specify behavior of GLEventListener Exceptions occurring while GLAutoDrawable processing [part-1]
Implements Specification as described on 'Bug 1039 Comment 1'
<https://jogamp.org/bugzilla/show_bug.cgi?id=1039#c1>
TODO:
- Offthread exception handler
++++
GLDrawableHelper is used in all GLAutoDrawable implementations
and for most operations.
GLAutoDrawable/GLDrawableHelper invoke(..) method:
- invoke(..) forwards a caught exception
- if blocking, it forwards an exception
happening within the passed GLRunnable(s).
Here the exception is caught, printed
and then thrown by invoke itself.
- if non-blocking, an exception
happening within the passed GLRunnable(s)
will be thrown in the thread issuing it's execution,
i.e. display() call.
Here the exception is not caught and simply thrown
by the GLRunnable.
GLAutoDrawable.destroy() -> GLDrawableHelper.disposeGL(..) method:
- disposeAllGLEventListener() being invoked by disposeGL(..),
catches exception thrown by GLEventListener.dispose(..)
and prints them to stderr.
The first caught exception is re-thrown at the end as an GLException.
- disposeGL() catches re-thrown GLException by disposeAllGLEventListener()
for GLEventListener.dispose(..)
and re-throws it when operation is complete.
- disposeGL() catches an exception thrown at context destruction or release
and re-throws it when operation is complete.
An early exception at context.makeCurrent() is _not_ caught,
since it is the first operation which simply shall unwind the stack.
GLAutoDrawable.display() -> GLDrawableHelper.invokeGLImpl(..) method:
- invokeGLImpl(..) for display() follows disposeGL() mechanism, i.e.
it catches exception thrown at
GLEventListener's init(..), reshape(..) and display(..) methods
and re-throws it when operation is complete.
It also catches an exception thrown at context release
and re-throws it when operation is complete.
An early exception at context.makeCurrent() is _not_ caught,
since it is the first operation which simply shall unwind the stack.
++++
None of the above thrown exception shall be caught and suppressed
on the caller side.
If an operation must be completed while an exception is caught,
it shall be cached and re-thrown after the operations.
In case multiple exception at multiple places are caught within
an operation, they all shall be cached and the first one
shall be re-thrown.
In case of multiple exception from the same place,
i.e. a loop through all GLEventListener,
the first shall be cached and re-thrown after operation is completed.
It has to be determined, whether we like to dump the exceptions,
especially the ones who get suppressed in case of multiple exceptions.
Diffstat (limited to 'src/jogl/classes')
7 files changed, 190 insertions, 51 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 03c6d6929..c409bb253 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -205,10 +205,9 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS animatorPaused = false; } - if ( null != context ) { + GLException exceptionOnDisposeGL = null; + if( null != context ) { if( context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. try { if( !GLCanvas.this.isDisposed() ) { helper.disposeGL(GLCanvas.this, context, true); @@ -216,28 +215,50 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS context.destroy(); } } catch (final GLException gle) { - gle.printStackTrace(); + exceptionOnDisposeGL = gle; } } context = null; } - if ( null != drawable ) { - drawable.setRealized(false); + + Throwable exceptionOnUnrealize = null; + if( null != drawable ) { + try { + drawable.setRealized(false); + } catch( final Throwable re ) { + exceptionOnUnrealize = re; + } drawable = null; } - if( 0 != x11Window) { - SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); - x11Window = 0; - } else if( 0 != gdkWindow) { - SWTAccessor.destroyGDKWindow(gdkWindow); - gdkWindow = 0; + + Throwable exceptionOnDeviceClose = null; + try { + if( 0 != x11Window) { + SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); + x11Window = 0; + } else if( 0 != gdkWindow) { + SWTAccessor.destroyGDKWindow(gdkWindow); + gdkWindow = 0; + } + screen.getDevice().close(); + } catch (final Throwable re) { + exceptionOnDeviceClose = re; } - screen.getDevice().close(); if (animatorPaused) { animator.resume(); } + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + if( null != exceptionOnDeviceClose ) { + throw GLException.newGLException(exceptionOnDeviceClose); + } } finally { _lock.unlock(); } diff --git a/src/jogl/classes/javax/media/opengl/GLException.java b/src/jogl/classes/javax/media/opengl/GLException.java index 6a287c969..15e9cddac 100644 --- a/src/jogl/classes/javax/media/opengl/GLException.java +++ b/src/jogl/classes/javax/media/opengl/GLException.java @@ -41,7 +41,7 @@ package javax.media.opengl; /** A generic exception for OpenGL errors used throughout the binding as a substitute for {@link RuntimeException}. */ - +@SuppressWarnings("serial") public class GLException extends RuntimeException { /** Constructs a GLException object. */ public GLException() { @@ -65,4 +65,16 @@ public class GLException extends RuntimeException { public GLException(final Throwable cause) { super(cause); } + + /** Constructs a GLException object with the specified root + cause with a decorating message including the current thread name. */ + public static GLException newGLException(final Throwable t) { + return new GLException("Caught "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName(), t); + } + + /** Dumps a Throwable in a decorating message including the current thread name, and stack trace. */ + public static void dumpThrowable(final Throwable t) { + System.err.println("Caught "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName()); + t.printStackTrace(); + } } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index 0599c1086..ca5cf0e45 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -1232,26 +1232,32 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing animatorPaused = false; } + GLException exceptionOnDisposeGL = null; + // OLS will be detached by disposeGL's context destruction below if( null != context ) { if( context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. try { helper.disposeGL(GLCanvas.this, context, true); if(DEBUG) { System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context); } } catch (final GLException gle) { - gle.printStackTrace(); + exceptionOnDisposeGL = gle; } } context = null; } + + Throwable exceptionOnUnrealize = null; if( null != drawable ) { - drawable.setRealized(false); - if(DEBUG) { - System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); + try { + drawable.setRealized(false); + if(DEBUG) { + System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); + } + } catch( final Throwable re ) { + exceptionOnUnrealize = re; } drawable = null; } @@ -1260,6 +1266,14 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing animator.resume(); } + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + if(DEBUG) { System.err.println(getThreadName()+": dispose() - END, animator "+animator); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 6e6bb1cad..d08839b7f 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -1338,20 +1338,33 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if ( null != backend ) { final GLContext _context = backend.getContext(); final boolean backendDestroy = !backend.isUsingOwnLifecycle(); + + GLException exceptionOnDisposeGL = null; if( null != _context && _context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. try { helper.disposeGL(GLJPanel.this, _context, !backendDestroy); } catch (final GLException gle) { - gle.printStackTrace(); + exceptionOnDisposeGL = gle; } } + Throwable exceptionBackendDestroy = null; if ( backendDestroy ) { - backend.destroy(); + try { + backend.destroy(); + } catch( final Throwable re ) { + exceptionBackendDestroy = re; + } backend = null; isInitialized = false; } + + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionBackendDestroy ) { + throw GLException.newGLException(exceptionBackendDestroy); + } } } }; diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java index 45240df29..016d07ed6 100644 --- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -356,25 +356,48 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe preserveGLStateAtDestroy(false); preserveGLEventListenerState(); } + + GLException exceptionOnDisposeGL = null; if( null != context ) { if( context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. try { helper.disposeGL(this, context, true); } catch (final GLException gle) { - gle.printStackTrace(); + exceptionOnDisposeGL = gle; } } context = null; } + + final AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + Throwable exceptionOnUnrealize = null; if( null != drawable ) { - final AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); - drawable.setRealized(false); + try { + drawable.setRealized(false); + } catch( final Throwable re ) { + exceptionOnUnrealize = re; + } drawable = null; + } + + Throwable exceptionOnDeviceClose = null; + try { if( ownsDevice ) { device.close(); } + } catch (final Throwable re) { + exceptionOnDeviceClose = re; + } + + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + if( null != exceptionOnDeviceClose ) { + throw GLException.newGLException(exceptionOnDeviceClose); } } diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index 7d05174ce..6462b801b 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -511,15 +511,24 @@ public class GLDrawableHelper { * </p> * @param autoDrawable * @return the disposal count + * @throws GLException caused by {@link GLEventListener#dispose(GLAutoDrawable)} */ - public final int disposeAllGLEventListener(final GLAutoDrawable autoDrawable, final boolean remove) { + public final int disposeAllGLEventListener(final GLAutoDrawable autoDrawable, final boolean remove) throws GLException { + Throwable firstCaught = null; int disposeCount = 0; synchronized(listenersLock) { if( remove ) { for (int count = listeners.size(); 0 < count && 0 < listeners.size(); count--) { final GLEventListener listener = listeners.remove(0); if( !listenersToBeInit.remove(listener) ) { - listener.dispose(autoDrawable); + try { + listener.dispose(autoDrawable); + } catch (final Throwable t) { + if( null == firstCaught ) { + firstCaught = t; + } + GLException.dumpThrowable(t); + } disposeCount++; } } @@ -527,13 +536,23 @@ public class GLDrawableHelper { for (int i = 0; i < listeners.size(); i++) { final GLEventListener listener = listeners.get(i); if( !listenersToBeInit.contains(listener) ) { - listener.dispose(autoDrawable); + try { + listener.dispose(autoDrawable); + } catch (final Throwable t) { + if( null == firstCaught ) { + firstCaught = t; + } + GLException.dumpThrowable(t); + } listenersToBeInit.add(listener); disposeCount++; } } } } + if( null != firstCaught ) { + throw GLException.newGLException(firstCaught); + } return disposeCount; } @@ -1048,8 +1067,7 @@ public class GLDrawableHelper { try { forceNativeRelease(context); } catch (final Throwable ex) { - ex.printStackTrace(); - throw new GLException(ex); + throw GLException.newGLException(ex); } } exclusiveContextThread = t; @@ -1105,8 +1123,8 @@ public class GLDrawableHelper { final Runnable initAction) { if(null==context) { if (DEBUG) { - final Exception e = new GLException(getThreadName()+" Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext"); - e.printStackTrace(); + final Exception e = new GLException("Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext"); + GLException.dumpThrowable(e); } return; } @@ -1131,9 +1149,11 @@ public class GLDrawableHelper { * @param autoDrawable * @param context * @param destroyContext destroy context in the end while holding the lock + * @throws GLException caused by {@link GLEventListener#dispose(GLAutoDrawable)} or context closing + * */ public final void disposeGL(final GLAutoDrawable autoDrawable, - final GLContext context, final boolean destroyContext) { + final GLContext context, final boolean destroyContext) throws GLException { // Support for recursive makeCurrent() calls as well as calling // other drawables' display() methods from within another one's GLContext lastContext = GLContext.getCurrent(); @@ -1148,15 +1168,22 @@ public class GLDrawableHelper { } } + GLException disposeCaught = null; + Throwable contextCloseCaught = null; + int res; try { res = context.makeCurrent(); if (GLContext.CONTEXT_NOT_CURRENT != res) { if(GLContext.CONTEXT_CURRENT_NEW == res) { - throw new GLException(getThreadName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); + throw new GLException(GLDrawableHelper.getThreadName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); } if( listeners.size() > 0 && null != autoDrawable ) { - disposeAllGLEventListener(autoDrawable, false); + try { + disposeAllGLEventListener(autoDrawable, false); + } catch(final GLException t) { + disposeCaught = t; + } } } } finally { @@ -1167,9 +1194,9 @@ public class GLDrawableHelper { forceNativeRelease(context); } flushGLRunnables(); - } catch (final Exception e) { - System.err.println("Caught exception on thread "+getThreadName()); - e.printStackTrace(); + } catch (final Throwable t) { + GLException.dumpThrowable(t); + contextCloseCaught = t; } if (lastContext != null) { final int res2 = lastContext.makeCurrent(); @@ -1177,6 +1204,12 @@ public class GLDrawableHelper { lastInitAction.run(); } } + if( null != disposeCaught ) { + throw disposeCaught; + } + if( null != contextCloseCaught ) { + throw GLException.newGLException(contextCloseCaught); + } } } @@ -1186,6 +1219,9 @@ public class GLDrawableHelper { final Runnable initAction) { final Thread currentThread = Thread.currentThread(); + Throwable glEventListenerCaught = null; + Throwable contextReleaseCaught = null; + // Exclusive Cases: // 1: lock - unlock : default // 2: lock - - : exclusive, not locked yet @@ -1243,6 +1279,9 @@ public class GLDrawableHelper { if ( autoSwapBufferMode ) { drawable.swapBuffers(); } + } catch (final Throwable t) { + GLException.dumpThrowable(t); + glEventListenerCaught = t; } finally { if( _releaseExclusiveThread ) { exclusiveContextThread = null; @@ -1253,9 +1292,9 @@ public class GLDrawableHelper { if( releaseContext ) { try { context.release(); - } catch (final Exception e) { - System.err.println("Caught exception on thread "+getThreadName()); - e.printStackTrace(); + } catch (final Throwable t) { + GLException.dumpThrowable(t); + contextReleaseCaught = t; } } } @@ -1267,6 +1306,12 @@ public class GLDrawableHelper { lastInitAction.run(); } } + if( null != glEventListenerCaught ) { + throw GLException.newGLException(glEventListenerCaught); + } + if( null != contextReleaseCaught ) { + throw GLException.newGLException(contextReleaseCaught); + } } } @@ -1276,6 +1321,9 @@ public class GLDrawableHelper { final Runnable initAction) { final Thread currentThread = Thread.currentThread(); + Throwable glEventListenerCaught = null; + Throwable contextReleaseCaught = null; + // Exclusive Cases: // 1: lock - unlock : default // 2: lock - - : exclusive, not locked yet @@ -1347,6 +1395,9 @@ public class GLDrawableHelper { tdX = System.currentTimeMillis(); tdS = tdX - tdS; // swapBuffers } + } catch (final Throwable t) { + GLException.dumpThrowable(t); + glEventListenerCaught = t; } finally { if( _releaseExclusiveThread ) { exclusiveContextSwitch = 0; @@ -1359,9 +1410,9 @@ public class GLDrawableHelper { try { context.release(); ctxReleased = true; - } catch (final Exception e) { - System.err.println("Caught exception on thread "+getThreadName()); - e.printStackTrace(); + } catch (final Throwable t) { + GLException.dumpThrowable(t); + contextReleaseCaught = t; } } } @@ -1374,11 +1425,16 @@ public class GLDrawableHelper { lastInitAction.run(); } } + if( null != glEventListenerCaught ) { + throw GLException.newGLException(glEventListenerCaught); + } + if( null != contextReleaseCaught ) { + throw GLException.newGLException(contextReleaseCaught); + } } final long td = System.currentTimeMillis() - t0; System.err.println("td0 "+td+"ms, fps "+(1.0/(td/1000.0))+", td-makeCurrent: "+tdA+"ms, td-render "+tdR+"ms, td-swap "+tdS+"ms, td-release "+tdX+"ms, ctx claimed: "+ctxClaimed+", ctx release: "+ctxReleased+", ctx destroyed "+ctxDestroyed); } protected static String getThreadName() { return Thread.currentThread().getName(); } - } diff --git a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java index 2d0f8f70b..a779fed3b 100644 --- a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java @@ -322,10 +322,10 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } } if(null != tFBO) { - throw new GLException("GLFBODrawableImpl.reset(..) FBObject.reset(..) exception", tFBO); + throw GLException.newGLException(tFBO); } if(null != tGL) { - throw new GLException("GLFBODrawableImpl.reset(..) GLContext.release() exception", tGL); + throw GLException.newGLException(tGL); } if(DEBUG) { System.err.println("GLFBODrawableImpl.reset(newSamples "+newSamples+"): END "+this); |