All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jogamp.common.util.SyncedRingbuffer Maven / Gradle / Ivy

/**
 * 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 com.jogamp.common.util;

import java.io.PrintStream;
import java.lang.reflect.Array;

/**
 * Simple synchronized implementation of {@link Ringbuffer}.
 * 

* All methods utilize global synchronization. *

*

* Characteristics: *

    *
  • Read position points to the next read element.
  • *
  • Write position points to the next write element.
  • *
* * * *
EmptywritePos == readPossize == 0
FullwritePos == readPossize == capacity
*

*/ public class SyncedRingbuffer implements Ringbuffer { private final Object syncGlobal = new Object(); private /* final */ T[] array; // not final due to grow private /* final */ int capacity; // not final due to grow private int readPos; private int writePos; private int size; @Override public final String toString() { return "SyncedRingbuffer[filled "+size+" / "+capacity+", writePos "+writePos+", readPos "+readPos+"]"; } @Override public final void dump(final PrintStream stream, final String prefix) { stream.println(prefix+" "+toString()+" {"); for(int i=0; i * Example for a 10 element Integer array: *
     *  Integer[] source = new Integer[10];
     *  // fill source with content ..
     *  Ringbuffer rb = new SyncedRingbuffer(source);
     * 
*

*

* {@link #isFull()} returns true on the newly created full ring buffer. *

*

* Implementation will allocate an internal array with size of array copyFrom * and copy all elements from array copyFrom into the internal array. *

* @param copyFrom mandatory source array determining ring buffer's net {@link #capacity()} and initial content. * @throws IllegalArgumentException if copyFrom is null */ @SuppressWarnings("unchecked") public SyncedRingbuffer(final T[] copyFrom) throws IllegalArgumentException { capacity = copyFrom.length; array = (T[]) newArray(copyFrom.getClass(), capacity); resetImpl(true, copyFrom); } /** * Create an empty ring buffer instance w/ the given net capacity. *

* Example for a 10 element Integer array: *

     *  Ringbuffer rb = new SyncedRingbuffer(10, Integer[].class);
     * 
*

*

* {@link #isEmpty()} returns true on the newly created empty ring buffer. *

*

* Implementation will allocate an internal array of size capacity. *

* @param arrayType the array type of the created empty internal array. * @param capacity the initial net capacity of the ring buffer */ public SyncedRingbuffer(final Class arrayType, final int capacity) { this.capacity = capacity; this.array = newArray(arrayType, capacity); resetImpl(false, null /* empty, nothing to copy */ ); } @Override public final int capacity() { return capacity; } /** * {@inheritDoc} *

* Implementation sets read and write position to zero. *

*/ @Override public final void clear() { synchronized ( syncGlobal ) { resetImpl(false, null); for(int i=0; i * Implementation returns the element at the current read position and advances it, if not empty. *

*/ @Override public final T get() { try { return getImpl(false, false); } catch (final InterruptedException ie) { throw new RuntimeException(ie); } } /** * {@inheritDoc} *

* Implementation returns the element at the current read position and advances it, if not empty. *

*/ @Override public final T getBlocking() throws InterruptedException { return getImpl(true, false); } @Override public final T peek() { try { return getImpl(false, true); } catch (final InterruptedException ie) { throw new RuntimeException(ie); } } @Override public final T peekBlocking() throws InterruptedException { return getImpl(true, true); } private final T getImpl(final boolean blocking, final boolean peek) throws InterruptedException { synchronized( syncGlobal ) { if( 0 == size ) { if( blocking ) { while( 0 == size ) { syncGlobal.wait(); } } else { return null; } } final int localReadPos = readPos; final T r = array[localReadPos]; if( !peek ) { array[localReadPos] = null; size--; readPos = (localReadPos + 1) % capacity; syncGlobal.notifyAll(); // notify waiting putter } return r; } } /** * {@inheritDoc} *

* Implementation stores the element at the current write position and advances it, if not full. *

*/ @Override public final boolean put(final T e) { try { return putImpl(e, false, false); } catch (final InterruptedException ie) { throw new RuntimeException(ie); } } /** * {@inheritDoc} *

* Implementation stores the element at the current write position and advances it, if not full. *

*/ @Override public final void putBlocking(final T e) throws InterruptedException { if( !putImpl(e, false, true) ) { throw new InternalError("Blocking put failed: "+this); } } /** * {@inheritDoc} *

* Implementation keeps the element at the current write position and advances it, if not full. *

*/ @Override public final boolean putSame(final boolean blocking) throws InterruptedException { return putImpl(null, true, blocking); } private final boolean putImpl(final T e, final boolean sameRef, final boolean blocking) throws InterruptedException { synchronized( syncGlobal ) { if( capacity == size ) { if( blocking ) { while( capacity == size ) { syncGlobal.wait(); } } else { return false; } } final int localWritePos = writePos; if( !sameRef ) { array[localWritePos] = e; } size++; writePos = (localWritePos + 1) % capacity; syncGlobal.notifyAll(); // notify waiting getter return true; } } @Override public final void waitForFreeSlots(final int count) throws InterruptedException { synchronized ( syncGlobal ) { if( capacity - size < count ) { while( capacity - size < count ) { syncGlobal.wait(); } } } } @Override public final void growEmptyBuffer(final T[] newElements) throws IllegalStateException, IllegalArgumentException { synchronized ( syncGlobal ) { if( null == newElements ) { throw new IllegalArgumentException("newElements is null"); } @SuppressWarnings("unchecked") final Class arrayTypeInternal = (Class) array.getClass(); @SuppressWarnings("unchecked") final Class arrayTypeNew = (Class) newElements.getClass(); if( arrayTypeInternal != arrayTypeNew ) { throw new IllegalArgumentException("newElements array-type mismatch, internal "+arrayTypeInternal+", newElements "+arrayTypeNew); } if( 0 != size ) { throw new IllegalStateException("Buffer is not empty: "+this); } if( readPos != writePos ) { throw new InternalError("R/W pos not equal: "+this); } final int growAmount = newElements.length; final int newCapacity = capacity + growAmount; final T[] oldArray = array; final T[] newArray = newArray(arrayTypeInternal, newCapacity); // writePos == readPos writePos += growAmount; // warp writePos to the end of the new data location if( readPos > 0 ) { System.arraycopy(oldArray, 0, newArray, 0, readPos); } if( growAmount > 0 ) { System.arraycopy(newElements, 0, newArray, readPos, growAmount); } final int tail = capacity-readPos; if( tail > 0 ) { System.arraycopy(oldArray, readPos, newArray, writePos, tail); } size = growAmount; capacity = newCapacity; array = newArray; } } @Override public final void growFullBuffer(final int growAmount) throws IllegalStateException, IllegalArgumentException { synchronized ( syncGlobal ) { if( 0 > growAmount ) { throw new IllegalArgumentException("amount "+growAmount+" < 0 "); } if( capacity != size ) { throw new IllegalStateException("Buffer is not full: "+this); } if( readPos != writePos ) { throw new InternalError("R/W pos not equal: "+this); } @SuppressWarnings("unchecked") final Class arrayTypeInternal = (Class) array.getClass(); final int newCapacity = capacity + growAmount; final T[] oldArray = array; final T[] newArray = newArray(arrayTypeInternal, newCapacity); // writePos == readPos readPos += growAmount; // warp readPos to the end of the new data location if(writePos > 0) { System.arraycopy(oldArray, 0, newArray, 0, writePos); } final int tail = capacity-writePos; if( tail > 0 ) { System.arraycopy(oldArray, writePos, newArray, readPos, tail); } capacity = newCapacity; array = newArray; } } @SuppressWarnings("unchecked") private static T[] newArray(final Class arrayType, final int length) { return ((Object)arrayType == (Object)Object[].class) ? (T[]) new Object[length] : (T[]) Array.newInstance(arrayType.getComponentType(), length); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy