diff options
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/util/av/SyncedRingbuffer.java')
-rw-r--r-- | src/jogl/classes/jogamp/opengl/util/av/SyncedRingbuffer.java | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/src/jogl/classes/jogamp/opengl/util/av/SyncedRingbuffer.java b/src/jogl/classes/jogamp/opengl/util/av/SyncedRingbuffer.java new file mode 100644 index 000000000..5f5d69cf8 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/SyncedRingbuffer.java @@ -0,0 +1,286 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl.util.av; + +/** + * Simple synchronized ring buffer implementation. + * <p> + * Caller can chose whether to block until get / put is able to proceed or not. + * </p> + * <p> + * Caller can chose whether to pass an empty array and clear references at get, + * or using a preset array for circular access of same objects. + * </p> + * <p> + * Circular write position is equal to the read position if buffer is full or if buffer is empty. + * </p> + */ +public class SyncedRingbuffer<T> { + + protected final Object sync = new Object(); + protected final T[] array; + protected final int capacity; + protected int readPos; + protected int writePos; + protected int size; + + public final String toString() { + return "SyncedRingbuffer<?>[filled "+size+" / "+capacity+", writePos "+writePos+", readPos "+readPos+"]"; + } + + /** + * Create instance w/ the given array and it's capacity, e.g.: + * <pre> + * SyncedRingbuffer r = new SyncedRingbuffer<Integer>(new Integer[10]); + * </pre> + * <p> + * The array may either be clear, or preset w/ elements! + * </p> + * @param full if true, given array is assumed to be full, i.e. {@link #isFull()} will return true. + * @param array + */ + public SyncedRingbuffer(T[] array, boolean full) { + this.array = array; + this.capacity = array.length; + clearImpl(false); + if(full) { + size = capacity; + } + } + + public final int capacity() { + return capacity; + } + + /** + * Resets all ring buffer pointer to zero. + * <p> + * {@link #isEmpty()} will return <code>true</code> after calling this method. + * </p> + * <p> + * If <code>clearRefs</code> is true, all ring buffer slots will be set to <code>null</code>. + * </p> + * @param clearRefs if true, all ring buffer slots will be flushed, otherwise they remain intact. + */ + public final void clear(boolean clearRefs) { + synchronized ( sync ) { + clearImpl(clearRefs); + } + } + + private final void clearImpl(boolean clearRefs) { + readPos = 0; + writePos = 0; + size = 0; + if( clearRefs ) { + for(int i=0; i<capacity; i++) { + this.array[i] = null; + } + } + } + + /** Returns the number of elements in this ring buffer. */ + public final int size() { + synchronized ( sync ) { + return size; + } + } + + /** Returns the number of free slots available to put. */ + public final int getFreeSlots() { + synchronized ( sync ) { + return capacity - size; + } + } + + /** Returns true if this ring buffer is empty, otherwise false. */ + public final boolean isEmpty() { + synchronized ( sync ) { + return 0 == size; + } + } + + /** Returns true if this ring buffer is full, otherwise false. */ + public final boolean isFull() { + synchronized ( sync ) { + return capacity == size; + } + } + + /** + * Returns the oldest put element if available, otherwise null. + * <p> + * Impl. returns the element at the current read position + * and advances the read position - if available. + * </p> + * <p> + * If <code>clearRef</code> is true, the returned ring buffer slot will be set to <code>null</code>. + * </p> + * <p> + * Method is non blocking and returns immediately;. + * </p> + * @param clearRef if true, the returned ring buffer slot will be flushed, otherwise it remains intact. + * @return the oldest put element if available, otherwise null. + */ + public final T get(boolean clearRef) { + try { + return getImpl(clearRef, false, false); + } catch (InterruptedException ie) { throw new RuntimeException(ie); } + } + + /** + * Returns the oldest put element. + * <p> + * Impl. returns the element at the current read position + * and advances the read position. + * </p> + * <p> + * If <code>clearRef</code> is true, the returned ring buffer slot will be set to <code>null</code>. + * </p> + * <p> + * Methods blocks until an element becomes available via put. + * </p> + * @param clearRef if true, the returned ring buffer slot will be flushed, otherwise it remains intact. + * @return the oldest put element + * @throws InterruptedException + */ + public final T getBlocking(boolean clearRef) throws InterruptedException { + return getImpl(clearRef, true, false); + } + + public final T peek() throws InterruptedException { + return getImpl(false, false, true); + } + public final T peekBlocking() throws InterruptedException { + return getImpl(false, true, true); + } + + private final T getImpl(boolean clearRef, boolean blocking, boolean peek) throws InterruptedException { + synchronized ( sync ) { + if( 0 == size ) { + if( blocking ) { + while( 0 == size ) { + sync.wait(); + } + } else { + return null; + } + } + final T r = array[readPos]; + if( !peek ) { + if( clearRef ) { + array[readPos] = null; + } + readPos = (readPos + 1) % capacity; + size--; + sync.notifyAll(); // notify waiting putter + } + return r; + } + } + + /** + * Puts the element <code>e</code> at the current write position + * and advances the write position. + * <p> + * Returns true if successful, otherwise false in case buffer is full. + * </p> + * <p> + * Method is non blocking and returns immediately;. + * </p> + */ + public final boolean put(T e) { + try { + return putImpl(e, false, false); + } catch (InterruptedException ie) { throw new RuntimeException(ie); } + } + + /** + * Puts the element <code>e</code> at the current write position + * and advances the write position. + * <p> + * Method blocks until a free slot becomes available via get. + * </p> + * @throws InterruptedException + */ + public final void putBlocking(T e) throws InterruptedException { + if( !putImpl(e, false, true) ) { + throw new InternalError("Blocking put failed: "+this); + } + } + + /** + * Keeps the element at the current write position intact + * and advances the write position. + * <p> + * Returns true if successful, otherwise false in case buffer is full. + * </p> + * <p> + * If <code>blocking</code> is true, method blocks until a free slot becomes available via get. + * </p> + * @param blocking if true, wait until a free slot becomes available via get. + * @throws InterruptedException + */ + public final boolean putSame(boolean blocking) throws InterruptedException { + return putImpl(null, true, blocking); + } + + private final boolean putImpl(T e, boolean sameRef, boolean blocking) throws InterruptedException { + synchronized ( sync ) { + if( capacity <= size ) { + if( blocking ) { + while( capacity <= size ) { + sync.wait(); + } + } else { + return false; + } + } + if( !sameRef ) { + array[ writePos ] = e; + } + writePos = (writePos + 1) % capacity; + size++; + sync.notifyAll(); // notify waiting getter + return true; + } + } + + public final void waitForFreeSlots(int count) throws InterruptedException { + synchronized ( sync ) { + if( capacity - size < count ) { + while( capacity - size < count ) { + System.err.println("XXXX AAA XXX"); + sync.wait(); + } + } + } + } + +} |