diff options
author | Sven Gothel <[email protected]> | 2015-09-15 07:50:50 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2015-09-15 07:50:50 +0200 |
commit | 68c8e39fa8d6e700f0a99241c1a01a435b7f6284 (patch) | |
tree | d02cf789a06514da205f6da8a45f55538b019a0b /src/jogl/classes/jogamp/opengl | |
parent | cf9b31c30de3768447b20d6aa31ec1df00595871 (diff) |
Bug 1211: Hardening Condition-Wait from Spurious-Wakeups and unintended InterruptedException(s)
Below is an updated list of Condition-Wait classifications
as described in Bug 1211.
This list includes recent changes on GlueGen
regarding this Bug 1211.
A followup commit will address the unit tests.
- Noncancelable + Persistent-Wait
- GLMediaPlayerImpl.StreamWorker thread (changed)
- pauses thread in case of intr
- Cancelable + Persistent-Wait:
- LFRingbuffer.getImpl(..)
- LFRingbuffer.waitForFreeSlots(int)
- SyncedRingbuffer.getImpl(..)
- SyncedRingbuffer.waitForFreeSlots(int)
- FunctionTask.invokeOnNewThread(..) (changed)
- RunnableTask.invokeOnNewThread(..) (changed)
- SharedResourceRunner.run()
- SharedResourceRunner.doAndWait() (changed)
- SharedResourceRunner.start() (changed)
- SharedResourceRunner.stop() (changed)
- GLMediaPlayerImpl.StreamWorker ctor (changed)
- GLMediaPlayerImpl caller thread actions do*() (changed)
- AndroidGLMediaPlayerAPI14.getNextTextureImpl(..) (changed)
- DisplayImpl.enqueueEvent(..) (changed)
-> Persistent-Wait
-> Cancels wait and NEWTEvent
-> dispatchMessage(NEWTEventTask): always notifyCaller!
- GLDrawableHelper.invoke(..) (changed)
- DefaultEDTUtil.waitUntilIdle() (changed)
- DefaultEDTUtil.waitUntilStopped() (changed)
- DefaultEDTUtil.invokeImpl(..) (changed)
- DefaultEDTUtil.NEDT.run(..) (changed)
- AWTEDTUtil.waitUntilStopped(..) (changed)
- AWTEDTUtil.invokeImpl(..) (changed)
- AWTEDTUtil.NEDT.run(..) (changed)
- SWTEDTUtil.invokeImpl(..) (changed)
- SWTEDTUtil.waitUntilStopped(..) (changed)
- SWTEDTUtil.NEDT.run(..) (changed)
- GLWorkerThread.invokeAndWait(..)
- GLWorkerThread.start() (changed)
- GLWorkerThread.WorkerRunnable.run() (changed)
- Animator.run() (changed)
- AnimatorBase.finishLifecycleAction() (changed)
- OSXUtil.RunOnMainThread(..) (changed)
- SingletonInstanceServerSocket.Server.shutdown() (changed)
- SingletonInstanceServerSocket.Server.start() (changed)
- com.jogamp.audio.windows.waveout.Mixer.shutdown() (changed)
- Extending/Using InterruptSource.Thread (changed)
- DefaultEDTUtil.NEDT
- AWTEDTUtil.NEDT
- SWTEDTUtil.NEDT
- GLWorkerThread.thread
- Mixer.FillerThread
- Mixer.MixerThread
- Using InterruptSource.Thread (changed)
- TempFileCache
- LauncherTempFileCache
- Animator.thread
- SingletonInstanceServerSocket.Server.serverThread
Deprecated:
- FunctionTask.invoke(..) (changed)
-> on current thread, no wait -> deprecated
- RunnableTask.invoke(..) (changed)
-> on current thread, no wait -> deprecated
Diffstat (limited to 'src/jogl/classes/jogamp/opengl')
7 files changed, 253 insertions, 152 deletions
diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java index 19fd6c7e1..9d1490300 100644 --- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -51,6 +51,7 @@ import com.jogamp.opengl.GLRunnable; import com.jogamp.opengl.GLSharedContextSetter; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.RunnableTask; import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.opengl.GLAutoDrawableDelegate; import com.jogamp.opengl.GLEventListenerState; @@ -598,6 +599,44 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe return helper.getExclusiveContextThread(); } + /** + * Invokes given {@code runnable} on current thread outside of a probable claimed exclusive thread, + * i.e. releases the exclusive thread, executes the runnable and reclaims it. + * FIXME: Promote to GLAutoDrawable! + * + * @param runnable the {@link Runnable} to execute on the new thread. + * The runnable <b>must exit</b>, i.e. not loop forever. + * + * @see #setExclusiveContextThread(Thread, GLContext) + * + * @since 2.3.2 + */ + public final void invokeOnCurrentThread(final Runnable runnable) { + helper.runOutsideOfExclusiveContextThread(context, runnable); + } + /** + * Invokes given {@code runnable} on current thread outside of a probable claimed exclusive thread, + * i.e. releases the exclusive thread, executes the runnable and reclaims it. + * FIXME: Promote to GLAutoDrawable! + * + * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code> + * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately. + * @param runnable the {@link Runnable} to execute on the new thread. + * The runnable <b>must exit</b>, i.e. not loop forever. + * @return {@link RunnableTask} instance with execution details + * + * @see #setExclusiveContextThread(Thread, GLContext) + * + * @since 2.3.2 + */ + public final RunnableTask invokeOnNewThread(final ThreadGroup tg, final boolean waitUntilDone, final Runnable runnable) { + return RunnableTask.invokeOnNewThread(tg, null, waitUntilDone, + new Runnable() { + public final void run() { + helper.runOutsideOfExclusiveContextThread(context, runnable); + } }); + } + @Override public final boolean invoke(final boolean wait, final GLRunnable glRunnable) throws IllegalStateException { return helper.invoke(this, wait, glRunnable); diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index e9ee46a51..6bc93bf36 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -61,6 +61,7 @@ import com.jogamp.opengl.GLFBODrawable; import com.jogamp.opengl.GLRunnable; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.common.util.PropertyAccess; /** Encapsulates the implementation of most of the GLAutoDrawable's @@ -874,9 +875,8 @@ public class GLDrawableHelper { return false; } - GLRunnableTask rTask = null; + final GLRunnableTask rTask; final Object rTaskLock = new Object(); - Throwable throwable = null; synchronized(rTaskLock) { boolean deferredHere; synchronized(glRunnablesLock) { @@ -910,13 +910,13 @@ public class GLDrawableHelper { drawable.display(); } else if( wait ) { try { - rTaskLock.wait(); // free lock, allow execution of rTask + while( rTask.isInQueue() ) { + rTaskLock.wait(); // free lock, allow execution of rTask + } } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rTask.getThrowable(); + throw new InterruptedRuntimeException(ie); } + final Throwable throwable = rTask.getThrowable(); if(null!=throwable) { throw new RuntimeException(throwable); } @@ -941,9 +941,8 @@ public class GLDrawableHelper { } final int count = newGLRunnables.size(); - GLRunnableTask rTask = null; + final GLRunnableTask rTask; final Object rTaskLock = new Object(); - Throwable throwable = null; synchronized(rTaskLock) { boolean deferredHere; synchronized(glRunnablesLock) { @@ -981,13 +980,13 @@ public class GLDrawableHelper { drawable.display(); } else if( wait ) { try { - rTaskLock.wait(); // free lock, allow execution of rTask + while( rTask.isInQueue() ) { + rTaskLock.wait(); // free lock, allow execution of rTask + } } catch (final InterruptedException ie) { - throwable = ie; - } - if(null==throwable) { - throwable = rTask.getThrowable(); + throw new InterruptedRuntimeException(ie); } + final Throwable throwable = rTask.getThrowable(); if(null!=throwable) { throw new RuntimeException(throwable); } @@ -1082,6 +1081,21 @@ public class GLDrawableHelper { return exclusiveContextThread; } + /** + * Runs given {@code runnable} outside of a probable claimed exclusive thread, + * i.e. releases the exclusive thread, executes the runnable and reclaims it. + * @see #setExclusiveContextThread(Thread, GLContext) + * @since 2.3.2 + */ + public final void runOutsideOfExclusiveContextThread(final GLContext context, final Runnable runnable) { + final Thread t = setExclusiveContextThread(null, context); + try { + runnable.run(); + } finally { + setExclusiveContextThread(t, context); + } + } + private static final ThreadLocal<WeakReference<Runnable>> perThreadInitAction = new ThreadLocal<WeakReference<Runnable>>(); private static final Runnable getLastInitAction() { final WeakReference<Runnable> lastInitActionWR = perThreadInitAction.get(); diff --git a/src/jogl/classes/jogamp/opengl/GLRunnableTask.java b/src/jogl/classes/jogamp/opengl/GLRunnableTask.java index 1a6024bfa..cfe3df95d 100644 --- a/src/jogl/classes/jogamp/opengl/GLRunnableTask.java +++ b/src/jogl/classes/jogamp/opengl/GLRunnableTask.java @@ -29,6 +29,7 @@ package jogamp.opengl; import com.jogamp.opengl.GLRunnable; +import com.jogamp.common.ExceptionUtils; import com.jogamp.opengl.GLAutoDrawable; /** @@ -61,7 +62,7 @@ public class GLRunnableTask implements GLRunnable { } catch (final Throwable t) { runnableException = t; if(catchExceptions) { - runnableException.printStackTrace(); + ExceptionUtils.dumpThrowable("", runnableException); } else { throw new RuntimeException(runnableException); } @@ -75,7 +76,7 @@ public class GLRunnableTask implements GLRunnable { } catch (final Throwable t) { runnableException = t; if(catchExceptions) { - runnableException.printStackTrace(); + ExceptionUtils.dumpThrowable("", runnableException); } else { throw new RuntimeException(runnableException); } diff --git a/src/jogl/classes/jogamp/opengl/GLWorkerThread.java b/src/jogl/classes/jogamp/opengl/GLWorkerThread.java index c03cdea02..3d0adfc9c 100644 --- a/src/jogl/classes/jogamp/opengl/GLWorkerThread.java +++ b/src/jogl/classes/jogamp/opengl/GLWorkerThread.java @@ -43,6 +43,9 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.opengl.GLContext; /** Singleton thread upon which all OpenGL work is performed by @@ -78,15 +81,18 @@ public class GLWorkerThread { synchronized (GLWorkerThread.class) { if (!started) { lock = new Object(); - thread = new Thread(new WorkerRunnable(), - "JOGL-GLWorkerThread-"); + final WorkerRunnable worker = new WorkerRunnable(); + thread = new InterruptSource.Thread(null, worker, "JOGL-GLWorkerThread-"); thread.setDaemon(true); started = true; synchronized (lock) { thread.start(); try { - lock.wait(); + while(!worker.isRunning) { + lock.wait(); + } } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } } @@ -119,7 +125,7 @@ public class GLWorkerThread { // less cooperatively AccessController.doPrivileged(new PrivilegedAction() { public Object run() { - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { public void run() { Object lockTemp = lock; if (lockTemp == null) { @@ -177,7 +183,9 @@ public class GLWorkerThread { work = runnable; lockTemp.notifyAll(); - lockTemp.wait(); + while( null != work ) { + lockTemp.wait(); + } if (exception != null) { final Throwable localException = exception; exception = null; @@ -222,10 +230,13 @@ public class GLWorkerThread { protected static String getThreadName() { return Thread.currentThread().getName(); } static class WorkerRunnable implements Runnable { + volatile boolean isRunning = false; + @Override public void run() { // Notify starting thread that we're ready synchronized (lock) { + isRunning = true; lock.notifyAll(); } @@ -238,6 +249,7 @@ public class GLWorkerThread { // Avoid race conditions with wanting to release contexts on this thread lock.wait(1000); } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } if (GLContext.getCurrent() != null) { @@ -269,8 +281,7 @@ public class GLWorkerThread { final Runnable curAsync = queue.remove(0); curAsync.run(); } catch (final Throwable t) { - System.err.println(getThreadName()+": Exception occurred on JOGL OpenGL worker thread:"); - t.printStackTrace(); + ExceptionUtils.dumpThrowable("suppressed", t); // Noncancelable } } @@ -285,6 +296,7 @@ public class GLWorkerThread { } } } + isRunning = false; } } } diff --git a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java index de4a35a9b..0f6c1f875 100644 --- a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java +++ b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java @@ -38,6 +38,9 @@ import com.jogamp.nativewindow.AbstractGraphicsScreen; import com.jogamp.opengl.GLProfile; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.opengl.GLRendererQuirks; public class SharedResourceRunner implements Runnable { @@ -166,13 +169,18 @@ public class SharedResourceRunner implements Runnable { System.err.println("SharedResourceRunner.start() - start new Thread - "+getThreadName()); } resetState(); - thread = new Thread(this, getThreadName()+"-SharedResourceRunner"); + thread = new InterruptSource.Thread(null, this, getThreadName()+"-SharedResourceRunner"); thread.setDaemon(true); // Allow JVM to exit, even if this one is running thread.start(); - while (!running) { - try { + try { + while (!running) { this.wait(); - } catch (final InterruptedException ex) { } + } + } catch (final InterruptedException ex) { + // Cleanup + shouldRelease = true; + this.notifyAll(); + throw new InterruptedRuntimeException(ex); } } } @@ -188,11 +196,12 @@ public class SharedResourceRunner implements Runnable { synchronized (this) { shouldRelease = true; this.notifyAll(); - - while (running) { - try { + try { + while (running) { this.wait(); - } catch (final InterruptedException ex) { } + } + } catch (final InterruptedException ex) { + throw new InterruptedRuntimeException(ex); } } } @@ -213,7 +222,11 @@ public class SharedResourceRunner implements Runnable { ExceptionUtils.dumpStack(System.err); } if ( impl.isDeviceSupported(device) ) { - doAndWait(device, null); + try { + doAndWait(device, null); + } catch (final InterruptedException ex) { + throw new InterruptedRuntimeException(ex); + } sr = impl.mapGet(device); } if (DEBUG) { @@ -236,7 +249,11 @@ public class SharedResourceRunner implements Runnable { if (DEBUG) { System.err.println("SharedResourceRunner.releaseShared() " + device + ": trying - "+getThreadName()); } - doAndWait(null, device); + try { + doAndWait(null, device); + } catch (final InterruptedException ex) { + throw new InterruptedRuntimeException(ex); + } if (DEBUG) { System.err.println("SharedResourceRunner.releaseShared() " + device + ": done - "+getThreadName()); } @@ -246,7 +263,7 @@ public class SharedResourceRunner implements Runnable { return sr; } - private final void doAndWait(final AbstractGraphicsDevice initDevice, final AbstractGraphicsDevice releaseDevice) { + private final void doAndWait(final AbstractGraphicsDevice initDevice, final AbstractGraphicsDevice releaseDevice) throws InterruptedException { synchronized (this) { // wait until thread becomes ready to init new device, // pass the device and release the sync @@ -254,26 +271,41 @@ public class SharedResourceRunner implements Runnable { if (DEBUG) { System.err.println("SharedResourceRunner.doAndWait() START init: " + initDevice + ", release: "+releaseDevice+" - "+threadName); } - while (!ready && running) { - try { + try { + while (!ready && running) { this.wait(); - } catch (final InterruptedException ex) { } - } - if (DEBUG) { - System.err.println("SharedResourceRunner.doAndWait() set command: " + initDevice + ", release: "+releaseDevice+" - "+threadName); - } - this.initDevice = initDevice; - this.releaseDevice = releaseDevice; - this.notifyAll(); + } + if (DEBUG) { + System.err.println("SharedResourceRunner.doAndWait() set command: " + initDevice + ", release: "+releaseDevice+" - "+threadName); + } + this.initDevice = initDevice; + this.releaseDevice = releaseDevice; + this.notifyAll(); - // wait until thread has init/released the device - while ( running && ( !ready || null != this.initDevice || null != this.releaseDevice ) ) { - try { + // wait until thread has init/released the device + while ( running && ( !ready || null != this.initDevice || null != this.releaseDevice ) ) { this.wait(); - } catch (final InterruptedException ex) { } + } + } catch (final InterruptedException ex) { + final InterruptedException ex2 = SourcedInterruptedException.wrap(ex); + if (DEBUG) { + System.err.println("SharedResourceRunner.doAndWait() INTERRUPT init: " + initDevice + ", release: "+releaseDevice+" - "+threadName); + ExceptionUtils.dumpThrowable("", ex2); + } + // Cleanup initDevice due to exception! + final AbstractGraphicsDevice _initDevice = this.initDevice; + if( null != _initDevice ) { + if (DEBUG) { + System.err.println("SharedResourceRunner.doAndWait() Cleanup init: " + _initDevice + " -> release: "+this.releaseDevice+" - "+threadName); + } + this.releaseDevice = _initDevice; + this.initDevice = null; + this.notifyAll(); + } + throw ex2; } if (DEBUG) { - System.err.println("SharedResourceRunner.initializeAndWait END init: " + initDevice + ", release: "+releaseDevice+" - "+threadName); + System.err.println("SharedResourceRunner.doAndWait() END init: " + initDevice + ", release: "+releaseDevice+" - "+threadName); } } // done @@ -292,19 +324,18 @@ public class SharedResourceRunner implements Runnable { while (!shouldRelease) { try { - // wait for stop or init + // wait until call-thread issues stop or init/released a device ready = true; if (DEBUG) { System.err.println("SharedResourceRunner.run(): READY - " + threadName); } notifyAll(); - this.wait(); + while ( !shouldRelease && null == initDevice && null == releaseDevice ) { + this.wait(); + } } catch (final InterruptedException ex) { shouldRelease = true; - if(DEBUG) { - System.err.println("SharedResourceRunner.run(): INTERRUPTED - "+threadName); - ex.printStackTrace(); - } + ExceptionUtils.dumpThrowable("handled", SourcedInterruptedException.wrap(ex)); // cancelable } ready = false; @@ -321,7 +352,7 @@ public class SharedResourceRunner implements Runnable { try { sr = impl.createSharedResource(initDevice); } catch (final Exception e) { - e.printStackTrace(); + ExceptionUtils.dumpThrowable("handled", e); } if (null != sr) { impl.mapPut(initDevice, sr); @@ -335,9 +366,10 @@ public class SharedResourceRunner implements Runnable { if (null != sr) { try { impl.releaseSharedResource(sr); - impl.mapPut(releaseDevice, null); } catch (final Exception e) { - e.printStackTrace(); + ExceptionUtils.dumpThrowable("handled", e); + } finally { + impl.mapPut(releaseDevice, null); } } } @@ -370,8 +402,7 @@ public class SharedResourceRunner implements Runnable { try { impl.releaseSharedResource(iter.next()); } catch (final Throwable t) { - System.err.println("Caught exception on thread "+getThreadName()); - t.printStackTrace(); + ExceptionUtils.dumpThrowable("", t); } } impl.clear(); diff --git a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java index da81922a4..09d2dfda0 100644 --- a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java +++ b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java @@ -33,7 +33,6 @@ import java.util.List; import com.jogamp.opengl.GL; import com.jogamp.opengl.GLES2; import com.jogamp.opengl.GLException; - import com.jogamp.common.os.AndroidVersion; import com.jogamp.common.os.Platform; import com.jogamp.opengl.util.TimeFrameI; @@ -364,7 +363,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } @Override - protected final int getNextTextureImpl(final GL gl, final TextureFrame nextFrame) { + protected final int getNextTextureImpl(final GL gl, final TextureFrame nextFrame) throws InterruptedException { int pts = TimeFrameI.INVALID_PTS; if(null != mp || null != cam) { final SurfaceTextureFrame sTexFrame = null != nextFrame ? (SurfaceTextureFrame) nextFrame : singleSTexFrame; @@ -398,12 +397,8 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { boolean update = updateSurface; if( !update ) { synchronized(updateSurfaceLock) { - if(!updateSurface) { // volatile OK. - try { - updateSurfaceLock.wait(); - } catch (final InterruptedException e) { - e.printStackTrace(); - } + while(!updateSurface) { // volatile OK. + updateSurfaceLock.wait(); // propagates InterruptedException } update = updateSurface; updateSurface = false; diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java index cfecbfd8d..1b19eced7 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java +++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java @@ -46,10 +46,14 @@ import com.jogamp.opengl.GLProfile; import jogamp.opengl.Debug; import com.jogamp.common.net.UriQueryProps; +import com.jogamp.common.ExceptionUtils; import com.jogamp.common.net.Uri; import com.jogamp.common.os.Platform; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.common.util.LFRingbuffer; import com.jogamp.common.util.Ringbuffer; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.util.TimeFrameI; import com.jogamp.opengl.util.av.AudioSink; @@ -365,7 +369,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { event_mask = addStateEventMask(event_mask, GLMediaPlayer.State.Paused); setState( State.Paused ); if( null != streamWorker ) { - streamWorker.doPause(); + streamWorker.doPause(true); } if( flush ) { resetAVPTSAndFlush(); @@ -414,7 +418,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { final State _state = state; setState( State.Paused ); if( null != streamWorker ) { - streamWorker.doPause(); + streamWorker.doPause(true); } // Adjust target .. if( msec >= duration ) { @@ -571,7 +575,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { this.vid = vid; this.aid = aid; - new Thread() { + new InterruptSource.Thread() { public void run() { try { // StreamWorker may be used, see API-doc of StreamWorker @@ -968,8 +972,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { * shall be <code>null</code> for audio only. * @return the last processed video PTS value, maybe {@link TimeFrameI#INVALID_PTS} if video frame is invalid or n/a. * Will be {@link TimeFrameI#END_OF_STREAM_PTS} if end of stream reached. + * @throws InterruptedException if waiting for next frame fails */ - protected abstract int getNextTextureImpl(GL gl, TextureFrame nextFrame); + protected abstract int getNextTextureImpl(GL gl, TextureFrame nextFrame) throws InterruptedException; protected final int getNextSingleThreaded(final GL gl, final TextureFrame nextFrame, final boolean[] gotVFrame) throws InterruptedException { final int pts; @@ -1064,7 +1069,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { * {@link GLMediaPlayerImpl#updateAttributes(int, int, int, int, int, int, int, float, int, int, int, String, String) updateAttributes(..)}, * the latter decides whether StreamWorker is being used. */ - class StreamWorker extends Thread { + class StreamWorker extends InterruptSource.Thread { private volatile boolean isRunning = false; private volatile boolean isActive = false; private volatile boolean isBlocked = false; @@ -1086,13 +1091,13 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { setDaemon(true); synchronized(this) { start(); - while( !isRunning ) { + try { this.notifyAll(); // wake-up startup-block - try { + while( !isRunning && !shallStop ) { this.wait(); // wait until started - } catch (final InterruptedException e) { - e.printStackTrace(); } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } } } @@ -1140,18 +1145,20 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { sharedGLCtx.release(); } } - public final synchronized void doPause() { + public final synchronized void doPause(final boolean waitUntilDone) { if( isActive ) { shallPause = true; - if( Thread.currentThread() != this ) { + if( java.lang.Thread.currentThread() != this ) { if( isBlocked && isActive ) { this.interrupt(); } - while( isActive && isRunning ) { + if( waitUntilDone ) { try { - this.wait(); // wait until paused + while( isActive && isRunning ) { + this.wait(); // wait until paused + } } catch (final InterruptedException e) { - e.printStackTrace(); + throw new InterruptedRuntimeException(e); } } } @@ -1160,14 +1167,16 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { public final synchronized void doResume() { if( isRunning && !isActive ) { shallPause = false; - if( Thread.currentThread() != this ) { - while( !isActive && !shallPause && isRunning ) { + if( java.lang.Thread.currentThread() != this ) { + try { this.notifyAll(); // wake-up pause-block - try { + while( !isActive && !shallPause && isRunning ) { this.wait(); // wait until resumed - } catch (final InterruptedException e) { - e.printStackTrace(); } + } catch (final InterruptedException e) { + final InterruptedException e2 = SourcedInterruptedException.wrap(e); + doPause(false); + throw new InterruptedRuntimeException(e2); } } } @@ -1175,17 +1184,17 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { public final synchronized void doStop() { if( isRunning ) { shallStop = true; - if( Thread.currentThread() != this ) { + if( java.lang.Thread.currentThread() != this ) { if( isBlocked && isRunning ) { this.interrupt(); } - while( isRunning ) { + try { this.notifyAll(); // wake-up pause-block (opt) - try { + while( isRunning ) { this.wait(); // wait until stopped - } catch (final InterruptedException e) { - e.printStackTrace(); } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } } } @@ -1203,48 +1212,48 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { this.notifyAll(); // wake-up ctor() } - while( !shallStop ){ - if( shallPause ) { - synchronized ( this ) { - if( sharedGLCtxCurrent ) { - postNextTextureImpl(sharedGLCtx.getGL()); - sharedGLCtx.release(); - } - while( shallPause && !shallStop ) { - isActive = false; - this.notifyAll(); // wake-up doPause() - try { - this.wait(); // wait until resumed - } catch (final InterruptedException e) { - if( !shallPause ) { - e.printStackTrace(); + while( !shallStop ) { + TextureFrame nextFrame = null; + try { + if( shallPause ) { + synchronized ( this ) { + if( sharedGLCtxCurrent ) { + postNextTextureImpl(sharedGLCtx.getGL()); + sharedGLCtx.release(); + } + while( shallPause && !shallStop ) { + isActive = false; + this.notifyAll(); // wake-up doPause() + try { + this.wait(); // wait until resumed + } catch (final InterruptedException e) { + if( !shallPause ) { + throw SourcedInterruptedException.wrap(e); + } } } + if( sharedGLCtxCurrent ) { + makeCurrent(sharedGLCtx); + preNextTextureImpl(sharedGLCtx.getGL()); + } + isActive = true; + this.notifyAll(); // wake-up doResume() } - if( sharedGLCtxCurrent ) { - makeCurrent(sharedGLCtx); - preNextTextureImpl(sharedGLCtx.getGL()); - } - isActive = true; - this.notifyAll(); // wake-up doResume() } - } - if( !sharedGLCtxCurrent && null != sharedGLCtx ) { - synchronized ( this ) { - if( null != sharedGLCtx ) { - makeCurrent( sharedGLCtx ); - preNextTextureImpl(sharedGLCtx.getGL()); - sharedGLCtxCurrent = true; - } - if( null == videoFramesFree ) { - throw new InternalError("XXX videoFramesFree is null"); + if( !sharedGLCtxCurrent && null != sharedGLCtx ) { + synchronized ( this ) { + if( null != sharedGLCtx ) { + makeCurrent( sharedGLCtx ); + preNextTextureImpl(sharedGLCtx.getGL()); + sharedGLCtxCurrent = true; + } + if( null == videoFramesFree ) { + throw new InternalError("XXX videoFramesFree is null"); + } } } - } - if( !shallStop ) { - TextureFrame nextFrame = null; - try { + if( !shallStop ) { isBlocked = true; final GL gl; if( STREAM_ID_NONE != vid ) { @@ -1260,7 +1269,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { if( TimeFrameI.INVALID_PTS != vPTS ) { if( null != nextFrame ) { if( STREAM_WORKER_DELAY > 0 ) { - Thread.sleep(STREAM_WORKER_DELAY); + java.lang.Thread.sleep(STREAM_WORKER_DELAY); } if( !videoFramesDecoded.put(nextFrame) ) { throw new InternalError("XXX: free "+videoFramesFree+", decoded "+videoFramesDecoded+", "+GLMediaPlayerImpl.this); @@ -1294,31 +1303,30 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } pauseImpl(true, GLMediaEventListener.EVENT_CHANGE_EOS); } - } catch (final InterruptedException e) { - isBlocked = false; - if( !shallStop && !shallPause ) { - streamErr = new StreamException("InterruptedException while decoding: "+GLMediaPlayerImpl.this.toString(), e); - } - } catch (final Throwable t) { - streamErr = new StreamException(t.getClass().getSimpleName()+" while decoding: "+GLMediaPlayerImpl.this.toString(), t); - } finally { - if( null != nextFrame ) { // put back - videoFramesFree.put(nextFrame); + } + } catch (final InterruptedException e) { + if( !isBlocked ) { // !shallStop && !shallPause + streamErr = new StreamException("InterruptedException while decoding: "+GLMediaPlayerImpl.this.toString(), + SourcedInterruptedException.wrap(e)); + } + isBlocked = false; + } catch (final Throwable t) { + streamErr = new StreamException(t.getClass().getSimpleName()+" while decoding: "+GLMediaPlayerImpl.this.toString(), t); + } finally { + if( null != nextFrame ) { // put back + videoFramesFree.put(nextFrame); + } + if( null != streamErr ) { + if( DEBUG ) { + ExceptionUtils.dumpThrowable("handled", streamErr); } - if( null != streamErr ) { - if( DEBUG ) { - final Throwable t = null != streamErr.getCause() ? streamErr.getCause() : streamErr; - System.err.println("Caught StreamException: "+t.getMessage()); - t.printStackTrace(); - } - // state transition incl. notification - synchronized ( this ) { - shallPause = true; - isActive = false; - this.notifyAll(); // wake-up potential do*() - } - pauseImpl(true, GLMediaEventListener.EVENT_CHANGE_ERR); + // state transition incl. notification + synchronized ( this ) { + shallPause = true; + isActive = false; + this.notifyAll(); // wake-up potential do*() } + pauseImpl(true, GLMediaEventListener.EVENT_CHANGE_ERR); } } } @@ -1387,7 +1395,6 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { if( wasUninitialized ) { event_mask |= GLMediaEventListener.EVENT_CHANGE_INIT; - setState( State.Initialized ); } if( STREAM_ID_AUTO == vid ) { vid = STREAM_ID_NONE; @@ -1444,10 +1451,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } if( wasUninitialized ) { if( null != streamWorker ) { + changeState(GLMediaEventListener.EVENT_CHANGE_ERR, GLMediaPlayer.State.Uninitialized); throw new InternalError("XXX: StreamWorker not null - "+this); } if( TEXTURE_COUNT_MIN < textureCount || STREAM_ID_NONE == vid ) { // Enable StreamWorker for 'audio only' as well (Bug 918). streamWorker = new StreamWorker(); + setState( State.Initialized ); } if( DEBUG ) { System.err.println("XXX Initialize @ updateAttributes: "+this); |