diff options
author | Sven Gothel <[email protected]> | 2014-08-06 05:59:08 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-08-06 05:59:08 +0200 |
commit | 88eef9e4eae8e63762252f1d11bca2bd0fde809b (patch) | |
tree | 1a70cb2f54ada3a654928ec30432d5fca6ef822e /src/jogl/classes/com/jogamp/opengl/util | |
parent | 07a4801f3b5bfd4fba9a1a4a542ce2f2eae4396a (diff) |
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).
+++
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/util')
5 files changed, 151 insertions, 49 deletions
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<GLAutoDrawable> drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { final GLAutoDrawable drawable = drawables.get(i); if (drawable instanceof JComponent) { @@ -73,13 +75,13 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { } else { try { drawable.display(); - } catch (final RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/Animator.java b/src/jogl/classes/com/jogamp/opengl/util/Animator.java index 634e83353..d9a957199 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Animator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Animator.java @@ -132,7 +132,7 @@ public class Animator extends AnimatorBase { @Override public void run() { - GLException displayCaught = null; + UncaughtAnimatorException displayCaught = null; try { synchronized (Animator.this) { @@ -161,9 +161,9 @@ public class Animator extends AnimatorBase { ectCleared = true; setDrawablesExclCtxState(false); try { - display(); // propagate exclusive change! - } catch (final Throwable t) { - displayCaught = GLException.newGLException(t); + display(); // propagate exclusive context -> 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<GLAutoDrawable> 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<GLAutoDrawable> 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.size(); i++) { + drawables.get(i).flushGLRunnables(); + } + } + + protected static interface Condition { /** * @return true if branching (continue waiting, action), otherwise false */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java index 7fa4011f8..6b1485a6a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java @@ -34,8 +34,11 @@ package com.jogamp.opengl.util; import java.util.ArrayList; + 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. */ @@ -44,18 +47,18 @@ class DefaultAnimatorImpl implements AnimatorBase.AnimatorImpl { @Override public void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { final GLAutoDrawable drawable = drawables.get(i); try { drawable.display(); - } catch (final RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java index 746b642c2..9ae880414 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java @@ -148,6 +148,8 @@ public class FPSAnimator extends AnimatorBase { @Override public void run() { + UncaughtAnimatorException displayCaught = null; + if( justStarted ) { justStarted = false; synchronized (FPSAnimator.this) { @@ -160,7 +162,7 @@ public class FPSAnimator extends AnimatorBase { shouldRun = false; // isAnimating:=false @ pause below } else { shouldRun = true; - setDrawablesExclCtxState(exclusiveContext); + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context FPSAnimator.this.notifyAll(); } if(DEBUG) { @@ -168,46 +170,76 @@ public class FPSAnimator extends AnimatorBase { } } } - if( shouldRun ) { - display(); - } else if( shouldStop ) { // STOP + if( shouldRun && !shouldStop ) { // RUN + try { + display(); + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; + shouldRun = false; + shouldStop = true; + } + } else if( !shouldRun && !shouldStop ) { // PAUSE if(DEBUG) { - System.err.println("FPSAnimator P4: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator pausing: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); } this.cancel(); - if( !alreadyStopped ) { - alreadyStopped = true; + if( !alreadyPaused ) { // PAUSE + alreadyPaused = true; if( exclusiveContext && !drawablesEmpty ) { setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> 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(); } } |