/** * 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(); } } } } }