aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/openal/sound3d/Context.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/jogamp/openal/sound3d/Context.java')
-rw-r--r--src/java/com/jogamp/openal/sound3d/Context.java240
1 files changed, 199 insertions, 41 deletions
diff --git a/src/java/com/jogamp/openal/sound3d/Context.java b/src/java/com/jogamp/openal/sound3d/Context.java
index 28e033e..2884f0c 100644
--- a/src/java/com/jogamp/openal/sound3d/Context.java
+++ b/src/java/com/jogamp/openal/sound3d/Context.java
@@ -34,6 +34,7 @@
package com.jogamp.openal.sound3d;
+import com.jogamp.common.ExceptionUtils;
import com.jogamp.common.util.locks.LockFactory;
import com.jogamp.common.util.locks.RecursiveLock;
import com.jogamp.openal.*;
@@ -43,25 +44,32 @@ import com.jogamp.openal.util.ALHelpers;
/**
* This class provides a Sound3D Context associated with a specified device.
*
- * @author Athomas Goldberg
+ * @author Athomas Goldberg, Sven Gothel, et al.
*/
-public class Context {
+public final class Context {
private final RecursiveLock lock = LockFactory.createRecursiveLock();
- private Device device;
- private ALCcontext alCtx;
+ private final Device device;
+ private volatile ALCcontext alCtx;
private boolean threadContextLocked;
private boolean hasALC_thread_local_context;
+ private static final ThreadLocal<Context> currentContext = new ThreadLocal<Context>();
+ /**
+ * Creates a new Context for a given {@link ALCcontext} for the specified device.
+ *
+ * @param realContext {@link ALCcontext} instance, maybe null
+ * @param device The device the Context belongs to, must be valid
+ */
public Context(final ALCcontext realContext, final Device device) {
this.device = device;
this.alCtx = realContext;
{
hasALC_thread_local_context = false;
final boolean v;
- if( makeCurrent() ) {
+ if( makeCurrent(false) ) {
v = AudioSystem3D.alc.alcIsExtensionPresent(null, ALHelpers.ALC_EXT_thread_local_context) ||
AudioSystem3D.alc.alcIsExtensionPresent(device.getALDevice(), ALHelpers.ALC_EXT_thread_local_context);
- release();
+ release(false);
} else {
v = false;
}
@@ -70,26 +78,84 @@ public class Context {
}
/**
- * Creates a new Context for a specified device.
+ * Creates a new Context for a specified device including native {@link ALCcontext} creation.
+ *
+ * @param device The device the Context is being created for, must be valid.
+ * @param attributes list of {@link ALCcontext} attributes for context creation, maybe empty or null
+ */
+ public Context(final Device device, final int[] attributes) {
+ this( createImpl(device.getALDevice(), attributes), device);
+ }
+
+ /**
+ * Creates a new {@link ALCcontext}.
*
- * @param device The device the Context is being created for.
+ * @param alDevice a valid {@link ALCdevice}
+ * @param attributes lost of {@link ALCcontext} attributes for context creation
*/
- public Context(final Device device) {
- this(AudioSystem3D.alc.alcCreateContext(device.getALDevice(), null), device);
+ private static ALCcontext createImpl(final ALCdevice alDevice, final int[] attributes) {
+ if( null != attributes && attributes.length > 0 ) {
+ return AudioSystem3D.alc.alcCreateContext(alDevice, attributes, 0);
+ } else {
+ return AudioSystem3D.alc.alcCreateContext(alDevice, null);
+ }
+ }
+
+ /**
+ * Creates the internal {@link ALCcontext} instance if {@link #getALContext()} is null
+ * @param attributes lost of {@link ALCcontext} attributes for context creation
+ * @return true if the internal context has been successfully created, otherwise false
+ */
+ public boolean create(final int[] attributes) {
+ lock.lock();
+ try {
+ if( null == alCtx ) {
+ alCtx = createImpl(device.getALDevice(), attributes);
+ return null != alCtx;
+ }
+ return false;
+ } finally {
+ lock.unlock();
+ }
}
/**
- * Returns the OpenAL context.
+ * Recreates the internal {@link ALCcontext} instance, i.e. destroys it first if {@link #getALContext()} not null.
+ * <p>
+ * Context is made current again if it was current before.
+ * </p>
+ * @param attributes lost of {@link ALCcontext} attributes for context creation
+ * @return true if the internal context has been successfully recreated and made current again if was current before, otherwise false
*/
- public ALCcontext getALContext() {
- return alCtx;
+ public boolean recreate(final int[] attributes) {
+ lock.lock();
+ try {
+ final boolean wasCurrent = this == getCurrentContext();
+ destroyImpl();
+ alCtx = createImpl(device.getALDevice(), attributes);
+ if( null != alCtx ) {
+ if( wasCurrent ) {
+ return makeCurrentImpl();
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } finally {
+ lock.unlock();
+ }
}
+ /** Returns the OpenAL {@link ALCcontext}. */
+ public ALCcontext getALContext() { return alCtx; }
+
/** Returns whether {@link #getALContext()} is valid, i.e. not null, e.g. not {@link #destroy()}'ed. */
public boolean isValid() { return null != alCtx; }
+ /** Return {@link ALC#alcGetError(ALCdevice)} using {@link #getDevice()}. */
public int getALCError() {
- return AudioSystem3D.alc.alcGetError(device.getALDevice());
+ return device.getALCError();
}
/**
@@ -98,16 +164,8 @@ public class Context {
public void destroy() {
lock.lock();
try {
- if( null != alCtx ) {
- if( threadContextLocked ) {
- AudioSystem3D.alExt.alcSetThreadContext(null);
- } else {
- AudioSystem3D.alc.alcMakeContextCurrent(null);
- }
- AudioSystem3D.alc.alcDestroyContext(alCtx);
- alCtx = null;
- }
- device = null;
+ destroyImpl();
+ currentContext.set(null);
// unroll lock !
while(lock.getHoldCount() > 1) {
lock.unlock();
@@ -116,38 +174,132 @@ public class Context {
lock.unlock();
}
}
+ private void destroyImpl() {
+ if( null != alCtx ) {
+ if( threadContextLocked ) {
+ AudioSystem3D.alExt.alcSetThreadContext(null);
+ } else {
+ AudioSystem3D.alc.alcMakeContextCurrent(null);
+ }
+ AudioSystem3D.alc.alcDestroyContext(alCtx);
+ alCtx = null;
+ }
+ }
+
+ /**
+ * Returns this thread current context.
+ * If no context is current, returns null.
+ *
+ * @return the context current on this thread, or null if no context is current.
+ * @see #makeCurrent()
+ * @see #release()
+ */
+ public static Context getCurrentContext() {
+ return currentContext.get();
+ }
+
+ /** Return the lock count of this context, i.e. 0 if not locked, 1 if locked once, >1 for recursive locks. */
+ public int getLockCount() {
+ return lock.getHoldCount();
+ }
/**
- * Makes this context current.
+ * Makes the audio context current on the calling thread.
+ * <p>
+ * Recursive call to {@link #makeCurrent()} and hence {@link #release()} are supported.
+ * </p>
+ * <p>
+ * At any point in time one context can only be current on one thread,
+ * and one thread can only have one context current.
+ * </p>
+ * @param throwException if true, throws ALException if {@link #getALContext()} is null, current thread holds another context or failed to natively make current
+ * @return true if {@link #getALContext()} is valid, current thread holds no other context and context successfully made current, otherwise false
+ * @see #release()
*/
- public boolean makeCurrent() {
- final boolean r;
+ public boolean makeCurrent(final boolean throwException) throws ALException {
lock.lock();
+
+ if( null == alCtx ) {
+ lock.unlock();
+ if( throwException ) {
+ throw new ALException("Invalid "+this);
+ }
+ return false;
+ }
+
+ // One context can only be current on one thread,
+ // and one thread can only have one context current!
+ final Context current = getCurrentContext();
+ if (current != null) {
+ if (current == this) { // implicit recursive locking, lock.getHoldCount() > 1
+ return true;
+ } else {
+ lock.unlock();
+ if( throwException ) {
+ throw new ALException("Current thread "+Thread.currentThread()+" holds another "+current+" while claiming this "+this);
+ }
+ return false;
+ }
+ }
+ final boolean r = makeCurrentImpl();
+ if( r ) {
+ currentContext.set(this);
+ } else {
+ lock.unlock();
+ if( throwException ) {
+ throw new ALException("Context make current failed "+this);
+ }
+ }
+ return r;
+ }
+ private boolean makeCurrentImpl() {
if( hasALC_thread_local_context ) {
threadContextLocked = true;
- r = AudioSystem3D.alExt.alcSetThreadContext(alCtx);
+ return AudioSystem3D.alExt.alcSetThreadContext(alCtx);
} else {
threadContextLocked = false;
- r = AudioSystem3D.alc.alcMakeContextCurrent(alCtx);
+ return AudioSystem3D.alc.alcMakeContextCurrent(alCtx);
}
- if( !r ) {
- lock.unlock();
- }
- return r;
}
/**
- * Release this context.
+ * Releases control of this audio context from the current thread, if implementation utilizes context locking.
+ * <p>
+ * Recursive call to {@link #makeCurrent()} and hence {@link #release()} are supported.
+ * </p>
+ * <p>
+ * If native release fails, internal lock is not released.
+ * </p>
+ * @param throwException if true, throws ALException if context has not been previously made current on current thread
+ * or native release failed.
+ * @return true if context has previously been made current on the current thread and successfully released, otherwise false
+ * @see #makeCurrent()
*/
- public boolean release() {
- final boolean r;
- if( threadContextLocked ) {
- r = AudioSystem3D.alExt.alcSetThreadContext(null);
- } else {
- r = AudioSystem3D.alc.alcMakeContextCurrent(null);
+ public boolean release(final boolean throwException) throws ALException {
+ if( !lock.isOwner( Thread.currentThread() ) ) {
+ if( throwException ) {
+ throw new ALException("Context not held on current thread "+Thread.currentThread()+", "+this);
+ }
+ return false;
+ }
+ if( lock.getHoldCount() == 1 ) {
+ final boolean r;
+ if( threadContextLocked ) {
+ r = AudioSystem3D.alExt.alcSetThreadContext(null);
+ } else {
+ r = AudioSystem3D.alc.alcMakeContextCurrent(null);
+ }
+ if( r ) {
+ currentContext.set(null);
+ } else {
+ if( throwException ) {
+ throw new ALException("Context release failed "+this);
+ }
+ return false; // skip unlock!
+ }
}
lock.unlock();
- return r;
+ return true;
}
/**
@@ -165,4 +317,10 @@ public class Context {
public Device getDevice() {
return device;
}
+
+ @Override
+ public String toString() {
+ final String alCtxStr = null != alCtx ? "0x"+Integer.toHexString(alCtx.hashCode()) : "null";
+ return "ALContext[this 0x"+Integer.toHexString(hashCode())+", alCtx "+alCtxStr+" lockCount "+lock.getHoldCount()+", on "+device+"]";
+ }
}