From 88eef9e4eae8e63762252f1d11bca2bd0fde809b Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Wed, 6 Aug 2014 05:59:08 +0200 Subject: Bug 1039 - Specify behavior of GLEventListener Exceptions occurring while GLAutoDrawable processing [part-3] Add GLAnimatorControl.UncaughtGLAnimatorExceptionHandler interface to optionally handle uncaught exception within an animator thread by the user. Implementation also requires to flush all enqueued GLRunnable instances via GLAutoDrawable.invoked(..) in case such exception occurs. Hence 'GLAutoDrawable.flushGLRunnables()' has been added. Only subsequent exceptions, which cannot be thrown are dumped to System.stderr. +++ Handling of exceptions during dispose() Exception in NEWT's disposeGL*() are also caught and re-thrown after the NEWT window has been destroyed in WindowImpl.destroyAction: - GLEventListener.dispose(..) - GLDrawableHelper.disposeAllGLEventListener(..) - GLDrawableHelper.disposeGL(..) - GLAutoDrawableBase.destroyImplInLock(..) - GLWindow.GLLifecycleHook.destroyActionInLock(..) - WindowImpl.destroyAction on NEWT-EDT - WindowImpl.destroy Further more, exceptions occuring in native windowing toolkit triggered destroy() are ignored: - GLAutoDrawableBase.defaultWindowDestroyNotifyOp(..) It has to be seen whether such exception handling for dispose() shall be added to AWT/SWT. +++ TestGLException01NEWT covers all GLEventListener exception cases on-thread and off-thread (via animator). +++ --- .../classes/com/jogamp/opengl/swt/GLCanvas.java | 5 ++ .../com/jogamp/opengl/util/AWTAnimatorImpl.java | 12 ++-- .../classes/com/jogamp/opengl/util/Animator.java | 29 ++++----- .../com/jogamp/opengl/util/AnimatorBase.java | 76 ++++++++++++++++++++-- .../jogamp/opengl/util/DefaultAnimatorImpl.java | 11 ++-- .../com/jogamp/opengl/util/FPSAnimator.java | 72 ++++++++++++++------ 6 files changed, 156 insertions(+), 49 deletions(-) (limited to 'src/jogl/classes/com/jogamp/opengl') diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index c409bb253..e33ceeefb 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -788,6 +788,11 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS return helper.invoke(this, wait, runnables); } + @Override + public void flushGLRunnables() { + helper.flushGLRunnables(); + } + @Override public void setAnimator(final GLAnimatorControl arg0) throws GLException { helper.setAnimator(arg0); diff --git a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java index 496fb88c0..62df3faca 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java @@ -41,12 +41,14 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; + import javax.swing.JComponent; import javax.swing.RepaintManager; import javax.swing.SwingUtilities; - import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.util.AnimatorBase.UncaughtAnimatorException; + /** Abstraction to factor out AWT dependencies from the Animator's implementation in a way that still allows the FPSAnimator to pick up this behavior if desired. */ @@ -61,7 +63,7 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { @Override public void display(final ArrayList drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i off! + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; stopIssued = true; isAnimating = false; break; // end pause loop @@ -189,15 +189,15 @@ public class Animator extends AnimatorBase { // Resume from pause or drawablesEmpty, // implies !pauseIssued and !drawablesEmpty isAnimating = true; - setDrawablesExclCtxState(exclusiveContext); + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context Animator.this.notifyAll(); } } // sync Animator.this if (!stopIssued) { try { display(); - } catch (final Throwable t) { - displayCaught = GLException.newGLException(t); + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; stopIssued = true; isAnimating = false; break; // end animation loop @@ -216,14 +216,13 @@ public class Animator extends AnimatorBase { } finally { if( exclusiveContext && !drawablesEmpty ) { setDrawablesExclCtxState(false); - display(); // propagate exclusive change! try { - display(); // propagate exclusive change! - } catch (final Throwable t) { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { if( null == displayCaught ) { - displayCaught = GLException.newGLException(t); + displayCaught = dre; } else { - GLException.newGLException(t).printStackTrace(); + dre.printStackTrace(); } } } @@ -237,12 +236,12 @@ public class Animator extends AnimatorBase { } stopIssued = false; pauseIssued = false; - animThread = null; isAnimating = false; - Animator.this.notifyAll(); if( null != displayCaught ) { - throw displayCaught; + handleUncaughtException(displayCaught); } + animThread = null; + Animator.this.notifyAll(); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java index a5bdb9350..ed23a78ba 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -67,8 +67,25 @@ public abstract class AnimatorBase implements GLAnimatorControl { */ public static final int MODE_EXPECT_AWT_RENDERING_THREAD = 1 << 0; - public interface AnimatorImpl { - void display(ArrayList drawables, boolean ignoreExceptions, boolean printExceptions); + + @SuppressWarnings("serial") + public static class UncaughtAnimatorException extends RuntimeException { + final GLAutoDrawable drawable; + public UncaughtAnimatorException(final GLAutoDrawable drawable, final Throwable cause) { + super(cause); + this.drawable = drawable; + } + public final GLAutoDrawable getGLAutoDrawable() { return drawable; } + } + + public static interface AnimatorImpl { + /** + * @param drawables + * @param ignoreExceptions + * @param printExceptions + * @throws UncaughtAnimatorException as caused by {@link GLAutoDrawable#display()} + */ + void display(ArrayList drawables, boolean ignoreExceptions, boolean printExceptions) throws UncaughtAnimatorException; boolean blockUntilDone(Thread thread); } @@ -83,6 +100,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { protected boolean printExceptions; protected boolean exclusiveContext; protected Thread userExclusiveContextThread; + protected UncaughtGLAnimatorExceptionHandler uncaughtExceptionHandler; protected FPSCounterImpl fpsCounter = new FPSCounterImpl(); private final static Class awtAnimatorImplClazz; @@ -313,11 +331,16 @@ public abstract class AnimatorBase implements GLAnimatorControl { } } final Thread dECT = enable ? ( null != _exclusiveContextThread ? _exclusiveContextThread : animThread ) : null ; + UncaughtAnimatorException displayCaught = null; if( propagateState ) { setDrawablesExclCtxState(enable); if( !enable ) { if( Thread.currentThread() == getThread() || Thread.currentThread() == _exclusiveContextThread ) { - display(); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; + } } else { final boolean resumed = isAnimating() ? false : resume(); int counter = 10; @@ -338,6 +361,13 @@ public abstract class AnimatorBase implements GLAnimatorControl { } if(DEBUG) { System.err.println("AnimatorBase.setExclusiveContextThread: all-GLAD Ok: "+validateDrawablesExclCtxState(dECT)+", "+this); + if( null != displayCaught ) { + System.err.println("AnimatorBase.setExclusiveContextThread: caught: "+displayCaught.getMessage()); + displayCaught.printStackTrace(); + } + } + if( null != displayCaught ) { + throw displayCaught; } return oldExclusiveContext; } @@ -412,7 +442,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { this to get the most optimized painting behavior for the set of components this Animator manages, in particular when multiple lightweight widgets are continually being redrawn. */ - protected final void display() { + protected final void display() throws UncaughtAnimatorException { impl.display(drawables, ignoreExceptions, printExceptions); fpsCounter.tickFPS(); } @@ -482,7 +512,43 @@ public abstract class AnimatorBase implements GLAnimatorControl { this.printExceptions = printExceptions; } - protected interface Condition { + @Override + public final UncaughtGLAnimatorExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } + + @Override + public final void setUncaughtExceptionHandler(final UncaughtGLAnimatorExceptionHandler handler) { + uncaughtExceptionHandler = handler; + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread to flush all animator + */ + protected final synchronized void handleUncaughtException(final UncaughtAnimatorException ue) { + if( null != uncaughtExceptionHandler ) { + try { + uncaughtExceptionHandler.uncaughtException(this, ue.getGLAutoDrawable(), ue.getCause()); + } catch (final Throwable t) { /* ignore intentionally */ } + flushGLRunnables(); + } else { + flushGLRunnables(); + throw ue; + } + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread to flush all animator + */ + protected final synchronized void flushGLRunnables() { + for (int i=0; i drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i off! + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; + shouldRun = false; + shouldStop = true; + } } - synchronized (FPSAnimator.this) { - if(DEBUG) { - System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + if( null == displayCaught ) { + synchronized (FPSAnimator.this) { + if(DEBUG) { + System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + } + isAnimating = false; + FPSAnimator.this.notifyAll(); } - animThread = null; - isAnimating = false; - FPSAnimator.this.notifyAll(); } } - } else { + } + if( shouldStop ) { // STOP if(DEBUG) { - System.err.println("FPSAnimator P5: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator stopping: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); } this.cancel(); - if( !alreadyPaused ) { // PAUSE - alreadyPaused = true; + if( !alreadyStopped ) { + alreadyStopped = true; if( exclusiveContext && !drawablesEmpty ) { setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + if( null == displayCaught ) { + displayCaught = dre; + } else { + dre.printStackTrace(); + } + } } synchronized (FPSAnimator.this) { if(DEBUG) { - System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + if( null != displayCaught ) { + System.err.println("AnimatorBase.setExclusiveContextThread: caught: "+displayCaught.getMessage()); + displayCaught.printStackTrace(); + } } isAnimating = false; + if( null != displayCaught ) { + handleUncaughtException(displayCaught); + } + animThread = null; FPSAnimator.this.notifyAll(); } } -- cgit v1.2.3