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 | |
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')
13 files changed, 402 insertions, 279 deletions
diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java index bbfe72b08..30b3fd278 100644 --- a/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java +++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java @@ -36,6 +36,9 @@ import java.io.*; import java.nio.*; import java.util.*; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; + // Needed only for NIO workarounds on CVM import java.lang.reflect.*; @@ -43,9 +46,10 @@ public class Mixer { // This class is a singleton private static Mixer mixer; - private volatile boolean shutdown; - private volatile Object shutdownLock = new Object(); - private volatile boolean shutdownDone; + volatile boolean fillerAlive; + volatile boolean mixerAlive; + volatile boolean shutdown; + volatile Object shutdownLock = new Object(); // Windows Event object private final long event; @@ -63,6 +67,9 @@ public class Mixer { private Mixer() { event = CreateEvent(); + fillerAlive = false; + mixerAlive = false; + shutdown = false; new FillerThread().start(); final MixerThread m = new MixerThread(); m.setPriority(Thread.MAX_PRIORITY - 1); @@ -118,50 +125,57 @@ public class Mixer { shutdown = true; SetEvent(event); try { - shutdownLock.wait(); + while(fillerAlive || mixerAlive) { + shutdownLock.wait(); + } } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } } } - class FillerThread extends Thread { + class FillerThread extends InterruptSource.Thread { FillerThread() { - super("Mixer Thread"); + super(null, null, "Mixer Thread"); } @Override public void run() { - while (!shutdown) { - final List<Track> curTracks = tracks; + fillerAlive = true; + try { + while (!shutdown) { + final List<Track> curTracks = tracks; - for (final Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { - final Track track = iter.next(); + for (final Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { + final Track track = iter.next(); + try { + track.fill(); + } catch (final IOException e) { + e.printStackTrace(); + remove(track); + } + } try { - track.fill(); - } catch (final IOException e) { - e.printStackTrace(); - remove(track); + // Run ten times per second + java.lang.Thread.sleep(100); + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); } } - - try { - // Run ten times per second - Thread.sleep(100); - } catch (final InterruptedException e) { - e.printStackTrace(); - } + } finally { + fillerAlive = false; } } } - class MixerThread extends Thread { + class MixerThread extends InterruptSource.Thread { // Temporary mixing buffer // Interleaved left and right channels float[] mixingBuffer; private final Vec3f temp = new Vec3f(); MixerThread() { - super("Mixer Thread"); + super(null, null, "Mixer Thread"); if (!initializeWaveOut(event)) { throw new InternalError("Error initializing waveout device"); } @@ -169,108 +183,113 @@ public class Mixer { @Override public void run() { - while (!shutdown) { - // Get the next buffer - final long mixerBuffer = getNextMixerBuffer(); - if (mixerBuffer != 0) { - ByteBuffer buf = getMixerBufferData(mixerBuffer); - - if (buf == null) { - // This is happening on CVM because - // JNI_NewDirectByteBuffer isn't implemented - // by default and isn't compatible with the - // JSR-239 NIO implementation (apparently) - buf = newDirectByteBuffer(getMixerBufferDataAddress(mixerBuffer), - getMixerBufferDataCapacity(mixerBuffer)); - } + mixerAlive = true; + try { + while (!shutdown) { + // Get the next buffer + final long mixerBuffer = getNextMixerBuffer(); + if (mixerBuffer != 0) { + ByteBuffer buf = getMixerBufferData(mixerBuffer); + + if (buf == null) { + // This is happening on CVM because + // JNI_NewDirectByteBuffer isn't implemented + // by default and isn't compatible with the + // JSR-239 NIO implementation (apparently) + buf = newDirectByteBuffer(getMixerBufferDataAddress(mixerBuffer), + getMixerBufferDataCapacity(mixerBuffer)); + } - if (buf == null) { - throw new InternalError("Couldn't wrap the native address with a direct byte buffer"); - } + if (buf == null) { + throw new InternalError("Couldn't wrap the native address with a direct byte buffer"); + } - // System.out.println("Mixing buffer"); + // System.out.println("Mixing buffer"); - // If we don't have enough samples in our mixing buffer, expand it - // FIXME: knowledge of native output rendering format - if ((mixingBuffer == null) || (mixingBuffer.length < (buf.capacity() / 2 /* bytes / sample */))) { - mixingBuffer = new float[buf.capacity() / 2]; - } else { - // Zap it - for (int i = 0; i < mixingBuffer.length; i++) { - mixingBuffer[i] = 0.0f; + // If we don't have enough samples in our mixing buffer, expand it + // FIXME: knowledge of native output rendering format + if ((mixingBuffer == null) || (mixingBuffer.length < (buf.capacity() / 2 /* bytes / sample */))) { + mixingBuffer = new float[buf.capacity() / 2]; + } else { + // Zap it + for (int i = 0; i < mixingBuffer.length; i++) { + mixingBuffer[i] = 0.0f; + } } - } - - // This assertion should be in place if we have stereo - if ((mixingBuffer.length % 2) != 0) { - final String msg = "FATAL ERROR: odd number of samples in the mixing buffer"; - System.out.println(msg); - throw new InternalError(msg); - } - // Run down all of the registered tracks mixing them in - final List<Track> curTracks = tracks; + // This assertion should be in place if we have stereo + if ((mixingBuffer.length % 2) != 0) { + final String msg = "FATAL ERROR: odd number of samples in the mixing buffer"; + System.out.println(msg); + throw new InternalError(msg); + } - for (final Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { - final Track track = iter.next(); - // Consider only playing tracks - if (track.isPlaying()) { - // First recompute its gain - final Vec3f pos = track.getPosition(); - final float leftGain = gain(pos, leftSpeakerPosition); - final float rightGain = gain(pos, rightSpeakerPosition); - // Now mix it in - int i = 0; - while (i < mixingBuffer.length) { - if (track.hasNextSample()) { - final float sample = track.nextSample(); - mixingBuffer[i++] = sample * leftGain; - mixingBuffer[i++] = sample * rightGain; - } else { - // This allows tracks to stall without being abruptly cancelled - if (track.done()) { - remove(track); + // Run down all of the registered tracks mixing them in + final List<Track> curTracks = tracks; + + for (final Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { + final Track track = iter.next(); + // Consider only playing tracks + if (track.isPlaying()) { + // First recompute its gain + final Vec3f pos = track.getPosition(); + final float leftGain = gain(pos, leftSpeakerPosition); + final float rightGain = gain(pos, rightSpeakerPosition); + // Now mix it in + int i = 0; + while (i < mixingBuffer.length) { + if (track.hasNextSample()) { + final float sample = track.nextSample(); + mixingBuffer[i++] = sample * leftGain; + mixingBuffer[i++] = sample * rightGain; + } else { + // This allows tracks to stall without being abruptly cancelled + if (track.done()) { + remove(track); + } + break; } - break; } } } - } - // Now that we have our data, send it down to the card - int outPos = 0; - for (int i = 0; i < mixingBuffer.length; i++) { - final short val = (short) mixingBuffer[i]; - buf.put(outPos++, (byte) val); - buf.put(outPos++, (byte) (val >> 8)); - } - if (!prepareMixerBuffer(mixerBuffer)) { - throw new RuntimeException("Error preparing mixer buffer"); - } - if (!writeMixerBuffer(mixerBuffer)) { - throw new RuntimeException("Error writing mixer buffer to device"); - } - } else { - // System.out.println("No mixer buffer available"); + // Now that we have our data, send it down to the card + int outPos = 0; + for (int i = 0; i < mixingBuffer.length; i++) { + final short val = (short) mixingBuffer[i]; + buf.put(outPos++, (byte) val); + buf.put(outPos++, (byte) (val >> 8)); + } + if (!prepareMixerBuffer(mixerBuffer)) { + throw new RuntimeException("Error preparing mixer buffer"); + } + if (!writeMixerBuffer(mixerBuffer)) { + throw new RuntimeException("Error writing mixer buffer to device"); + } + } else { + // System.out.println("No mixer buffer available"); - // Wait for a buffer to become available - if (!WaitForSingleObject(event)) { - throw new RuntimeException("Error while waiting for event object"); - } + // Wait for a buffer to become available + if (!WaitForSingleObject(event)) { + throw new RuntimeException("Error while waiting for event object"); + } - /* - try { - Thread.sleep(10); - } catch (InterruptedException e) { + /* + try { + Thread.sleep(10); + } catch (InterruptedException e) { + throw new InterruptedRuntimeException(e); + } + */ } - */ } - } - - // Need to shut down - shutdownWaveOut(); - synchronized(shutdownLock) { - shutdownLock.notifyAll(); + } finally { + mixerAlive = false; + // Need to shut down + shutdownWaveOut(); + synchronized(shutdownLock) { + shutdownLock.notifyAll(); + } } } diff --git a/src/jogl/classes/com/jogamp/opengl/GLAnimatorControl.java b/src/jogl/classes/com/jogamp/opengl/GLAnimatorControl.java index 549efd569..38b8f770b 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLAnimatorControl.java +++ b/src/jogl/classes/com/jogamp/opengl/GLAnimatorControl.java @@ -58,7 +58,8 @@ public interface GLAnimatorControl extends FPSCounter { * Any exception thrown by this method will be ignored. * </p> * @param animator the {@link GLAnimatorControl} - * @param drawable the causing {@link GLAutoDrawable} + * @param drawable the causing {@link GLAutoDrawable}, + * may be {@code null} in case {@link Throwable} caused unrelated to any {@link GLAutoDrawable}. * @param cause the uncaught exception * @see GLAnimatorControl#setUncaughtExceptionHandler(UncaughtExceptionHandler) * @since 2.2 diff --git a/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java b/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java index 5bf4c106c..20dc71958 100644 --- a/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java @@ -98,6 +98,7 @@ import jogamp.opengl.awt.AWTTilePainter; import jogamp.opengl.awt.Java2D; import jogamp.opengl.util.glsl.GLSLTextureRaster; +import com.jogamp.common.util.InterruptSource; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.awt.AWTEDTExecutor; import com.jogamp.common.util.locks.LockFactory; @@ -395,7 +396,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing */ public final boolean initializeBackend(final boolean offthread) { if( offthread ) { - new Thread(getThreadName()+"-GLJPanel_Init") { + new InterruptSource.Thread(null, null, getThreadName()+"-GLJPanel_Init") { public void run() { if( !isInitialized ) { initializeBackendImpl(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/Animator.java b/src/jogl/classes/com/jogamp/opengl/util/Animator.java index 066709316..10b0b6b5a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Animator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Animator.java @@ -40,6 +40,9 @@ package com.jogamp.opengl.util; +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLException; @@ -174,6 +177,9 @@ public class Animator extends AnimatorBase { try { Animator.this.wait(); } catch (final InterruptedException e) { + caughtException = new UncaughtAnimatorException(null, SourcedInterruptedException.wrap(e)); + stopIssued = true; + break; // end pause loop } if (wasPaused) { // resume from pause -> reset counter @@ -209,8 +215,7 @@ public class Animator extends AnimatorBase { } } catch(final ThreadDeath td) { if(DEBUG) { - System.err.println("Animator caught: "+td.getClass().getName()+": "+td.getMessage()); - td.printStackTrace(); + ExceptionUtils.dumpThrowable("", td); } caughtThreadDeath = td; } @@ -222,8 +227,7 @@ public class Animator extends AnimatorBase { if( null == caughtException ) { caughtException = dre; } else { - System.err.println("Animator.setExclusiveContextThread: caught: "+dre.getMessage()); - dre.printStackTrace(); + ExceptionUtils.dumpThrowable("(setExclusiveContextThread)", dre); } } } @@ -233,8 +237,7 @@ public class Animator extends AnimatorBase { if(DEBUG) { System.err.println("Animator stop on " + animThread.getName() + ": " + toString()); if( null != caughtException ) { - System.err.println("Animator caught: "+caughtException.getMessage()); - caughtException.printStackTrace(); + ExceptionUtils.dumpThrowable("", caughtException); } } stopIssued = false; @@ -291,13 +294,7 @@ public class Animator extends AnimatorBase { runnable = new MainLoop(); } fpsCounter.resetFPSCounter(); - final String threadName = getThreadName()+"-"+baseName; - Thread thread; - if(null==threadGroup) { - thread = new Thread(runnable, threadName); - } else { - thread = new Thread(threadGroup, runnable, threadName); - } + final Thread thread = new InterruptSource.Thread(threadGroup, runnable, getThreadName()+"-"+baseName); thread.setDaemon(false); // force to be non daemon, regardless of parent thread if(DEBUG) { final Thread ct = Thread.currentThread(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java index aafdf63f8..41b969551 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -40,6 +40,7 @@ import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLProfile; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptedRuntimeException; /** * Base implementation of GLAnimatorControl<br> @@ -596,7 +597,9 @@ public abstract class AnimatorBase implements GLAnimatorControl { notifyAll(); try { wait(pollPeriod); - } catch (final InterruptedException ie) { } + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } remaining -= System.currentTimeMillis() - t1 ; nok = waitCondition.eval(); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java index e6f5aaa2e..38bac06cc 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java @@ -40,6 +40,7 @@ package com.jogamp.opengl.util.awt; import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.InterruptSource; import com.jogamp.common.util.PropertyAccess; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.util.*; @@ -915,7 +916,7 @@ public class TextRenderer { // Run this on another thread than the AWT event queue to // make sure the call to Animator.stop() completes before // exiting - new Thread(new Runnable() { + new InterruptSource.Thread(null, new Runnable() { @Override public void run() { anim.stop(); 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); |