GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
SyncedRingbuffer.java
Go to the documentation of this file.
1/**
2 * Copyright 2013 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28
29package com.jogamp.common.util;
30
31import java.io.PrintStream;
32import java.lang.reflect.Array;
33
34/**
35 * Simple synchronized implementation of {@link Ringbuffer}.
36 * <p>
37 * All methods utilize global synchronization.
38 * </p>
39 * <p>
40 * Characteristics:
41 * <ul>
42 * <li>Read position points to the next read element.</li>
43 * <li>Write position points to the next write element.</li>
44 * </ul>
45 * <table border="1">
46 * <tr><td>Empty</td><td>writePos == readPos</td><td>size == 0</td></tr>
47 * <tr><td>Full</td><td>writePos == readPos</td><td>size == capacity</td></tr>
48 * </table>
49 * </p>
50 */
51public class SyncedRingbuffer<T> implements Ringbuffer<T> {
52
53 private final Object syncGlobal = new Object();
54 private /* final */ T[] array; // not final due to grow
55 private /* final */ int capacity; // not final due to grow
56 private int readPos;
57 private int writePos;
58 private int size;
59
60 @Override
61 public final String toString() {
62 return "SyncedRingbuffer<?>[filled "+size+" / "+capacity+", writePos "+writePos+", readPos "+readPos+"]";
63 }
64
65 @Override
66 public final void dump(final PrintStream stream, final String prefix) {
67 stream.println(prefix+" "+toString()+" {");
68 for(int i=0; i<capacity; i++) {
69 stream.println("\t["+i+"]: "+array[i]);
70 }
71 stream.println("}");
72 }
73
74 /**
75 * Create a full ring buffer instance w/ the given array's net capacity and content.
76 * <p>
77 * Example for a 10 element Integer array:
78 * <pre>
79 * Integer[] source = new Integer[10];
80 * // fill source with content ..
81 * Ringbuffer<Integer> rb = new SyncedRingbuffer<Integer>(source);
82 * </pre>
83 * </p>
84 * <p>
85 * {@link #isFull()} returns true on the newly created full ring buffer.
86 * </p>
87 * <p>
88 * Implementation will allocate an internal array with size of array <code>copyFrom</code>
89 * and copy all elements from array <code>copyFrom</code> into the internal array.
90 * </p>
91 * @param copyFrom mandatory source array determining ring buffer's net {@link #capacity()} and initial content.
92 * @throws IllegalArgumentException if <code>copyFrom</code> is <code>null</code>
93 */
94 @SuppressWarnings("unchecked")
95 public SyncedRingbuffer(final T[] copyFrom) throws IllegalArgumentException {
96 capacity = copyFrom.length;
97 array = (T[]) newArray(copyFrom.getClass(), capacity);
98 resetImpl(true, copyFrom);
99 }
100
101 /**
102 * Create an empty ring buffer instance w/ the given net <code>capacity</code>.
103 * <p>
104 * Example for a 10 element Integer array:
105 * <pre>
106 * Ringbuffer<Integer> rb = new SyncedRingbuffer<Integer>(10, Integer[].class);
107 * </pre>
108 * </p>
109 * <p>
110 * {@link #isEmpty()} returns true on the newly created empty ring buffer.
111 * </p>
112 * <p>
113 * Implementation will allocate an internal array of size <code>capacity</code>.
114 * </p>
115 * @param arrayType the array type of the created empty internal array.
116 * @param capacity the initial net capacity of the ring buffer
117 */
118 public SyncedRingbuffer(final Class<? extends T[]> arrayType, final int capacity) {
119 this.capacity = capacity;
120 this.array = newArray(arrayType, capacity);
121 resetImpl(false, null /* empty, nothing to copy */ );
122 }
123
124 @Override
125 public final int capacity() { return capacity; }
126
127 /**
128 * {@inheritDoc}
129 * <p>
130 * Implementation sets read and write position to zero.
131 * </p>
132 */
133 @Override
134 public final void clear() {
135 synchronized ( syncGlobal ) {
136 resetImpl(false, null);
137 for(int i=0; i<capacity; i++) {
138 this.array[i] = null;
139 }
140 }
141 }
142
143 @Override
144 public final void resetFull(final T[] copyFrom) throws IllegalArgumentException {
145 resetImpl(true, copyFrom);
146 }
147
148 private final void resetImpl(final boolean full, final T[] copyFrom) throws IllegalArgumentException {
149 synchronized ( syncGlobal ) {
150 if( null != copyFrom ) {
151 if( copyFrom.length != capacity() ) {
152 throw new IllegalArgumentException("copyFrom array length "+copyFrom.length+" != capacity "+this);
153 }
154 System.arraycopy(copyFrom, 0, array, 0, copyFrom.length);
155 } else if ( full ) {
156 throw new IllegalArgumentException("copyFrom array is null");
157 }
158 readPos = 0;
159 writePos = 0;
160 size = full ? capacity : 0;
161 }
162 }
163
164 @Override
165 public final int size() {
166 synchronized ( syncGlobal ) {
167 return size;
168 }
169 }
170
171 @Override
172 public final int getFreeSlots() {
173 synchronized ( syncGlobal ) {
174 return capacity - size;
175 }
176 }
177
178 @Override
179 public final boolean isEmpty() {
180 synchronized ( syncGlobal ) {
181 return 0 == size;
182 }
183 }
184
185 @Override
186 public final boolean isFull() {
187 synchronized ( syncGlobal ) {
188 return capacity == size;
189 }
190 }
191
192 /**
193 * {@inheritDoc}
194 * <p>
195 * Implementation returns the element at the current read position and advances it, if not empty.
196 * </p>
197 */
198 @Override
199 public final T get() {
200 try {
201 return getImpl(false, false);
202 } catch (final InterruptedException ie) { throw new RuntimeException(ie); }
203 }
204
205 /**
206 * {@inheritDoc}
207 * <p>
208 * Implementation returns the element at the current read position and advances it, if not empty.
209 * </p>
210 */
211 @Override
212 public final T getBlocking() throws InterruptedException {
213 return getImpl(true, false);
214 }
215
216 @Override
217 public final T peek() {
218 try {
219 return getImpl(false, true);
220 } catch (final InterruptedException ie) { throw new RuntimeException(ie); }
221 }
222 @Override
223 public final T peekBlocking() throws InterruptedException {
224 return getImpl(true, true);
225 }
226
227 private final T getImpl(final boolean blocking, final boolean peek) throws InterruptedException {
228 synchronized( syncGlobal ) {
229 if( 0 == size ) {
230 if( blocking ) {
231 while( 0 == size ) {
232 syncGlobal.wait();
233 }
234 } else {
235 return null;
236 }
237 }
238 final int localReadPos = readPos;
239 final T r = array[localReadPos];
240 if( !peek ) {
241 array[localReadPos] = null;
242 size--;
243 readPos = (localReadPos + 1) % capacity;
244 syncGlobal.notifyAll(); // notify waiting putter
245 }
246 return r;
247 }
248 }
249
250 /**
251 * {@inheritDoc}
252 * <p>
253 * Implementation stores the element at the current write position and advances it, if not full.
254 * </p>
255 */
256 @Override
257 public final boolean put(final T e) {
258 try {
259 return putImpl(e, false, false);
260 } catch (final InterruptedException ie) { throw new RuntimeException(ie); }
261 }
262
263 /**
264 * {@inheritDoc}
265 * <p>
266 * Implementation stores the element at the current write position and advances it, if not full.
267 * </p>
268 */
269 @Override
270 public final void putBlocking(final T e) throws InterruptedException {
271 if( !putImpl(e, false, true) ) {
272 throw new InternalError("Blocking put failed: "+this);
273 }
274 }
275
276 /**
277 * {@inheritDoc}
278 * <p>
279 * Implementation keeps the element at the current write position and advances it, if not full.
280 * </p>
281 */
282 @Override
283 public final boolean putSame(final boolean blocking) throws InterruptedException {
284 return putImpl(null, true, blocking);
285 }
286
287 private final boolean putImpl(final T e, final boolean sameRef, final boolean blocking) throws InterruptedException {
288 synchronized( syncGlobal ) {
289 if( capacity == size ) {
290 if( blocking ) {
291 while( capacity == size ) {
292 syncGlobal.wait();
293 }
294 } else {
295 return false;
296 }
297 }
298 final int localWritePos = writePos;
299 if( !sameRef ) {
300 array[localWritePos] = e;
301 }
302 size++;
303 writePos = (localWritePos + 1) % capacity;
304 syncGlobal.notifyAll(); // notify waiting getter
305 return true;
306 }
307 }
308
309 @Override
310 public final void waitForFreeSlots(final int count) throws InterruptedException {
311 synchronized ( syncGlobal ) {
312 if( capacity - size < count ) {
313 while( capacity - size < count ) {
314 syncGlobal.wait();
315 }
316 }
317 }
318 }
319
320
321 @Override
322 public final void growEmptyBuffer(final T[] newElements) throws IllegalStateException, IllegalArgumentException {
323 synchronized ( syncGlobal ) {
324 if( null == newElements ) {
325 throw new IllegalArgumentException("newElements is null");
326 }
327 @SuppressWarnings("unchecked")
328 final Class<? extends T[]> arrayTypeInternal = (Class<? extends T[]>) array.getClass();
329 @SuppressWarnings("unchecked")
330 final Class<? extends T[]> arrayTypeNew = (Class<? extends T[]>) newElements.getClass();
331 if( arrayTypeInternal != arrayTypeNew ) {
332 throw new IllegalArgumentException("newElements array-type mismatch, internal "+arrayTypeInternal+", newElements "+arrayTypeNew);
333 }
334 if( 0 != size ) {
335 throw new IllegalStateException("Buffer is not empty: "+this);
336 }
337 if( readPos != writePos ) {
338 throw new InternalError("R/W pos not equal: "+this);
339 }
340
341 final int growAmount = newElements.length;
342 final int newCapacity = capacity + growAmount;
343 final T[] oldArray = array;
344 final T[] newArray = newArray(arrayTypeInternal, newCapacity);
345
346 // writePos == readPos
347 writePos += growAmount; // warp writePos to the end of the new data location
348
349 if( readPos > 0 ) {
350 System.arraycopy(oldArray, 0, newArray, 0, readPos);
351 }
352 if( growAmount > 0 ) {
353 System.arraycopy(newElements, 0, newArray, readPos, growAmount);
354 }
355 final int tail = capacity-readPos;
356 if( tail > 0 ) {
357 System.arraycopy(oldArray, readPos, newArray, writePos, tail);
358 }
359 size = growAmount;
360
361 capacity = newCapacity;
362 array = newArray;
363 }
364 }
365
366 @Override
367 public final void growFullBuffer(final int growAmount) throws IllegalStateException, IllegalArgumentException {
368 synchronized ( syncGlobal ) {
369 if( 0 > growAmount ) {
370 throw new IllegalArgumentException("amount "+growAmount+" < 0 ");
371 }
372 if( capacity != size ) {
373 throw new IllegalStateException("Buffer is not full: "+this);
374 }
375 if( readPos != writePos ) {
376 throw new InternalError("R/W pos not equal: "+this);
377 }
378 @SuppressWarnings("unchecked")
379 final Class<? extends T[]> arrayTypeInternal = (Class<? extends T[]>) array.getClass();
380
381 final int newCapacity = capacity + growAmount;
382 final T[] oldArray = array;
383 final T[] newArray = newArray(arrayTypeInternal, newCapacity);
384
385 // writePos == readPos
386 readPos += growAmount; // warp readPos to the end of the new data location
387
388 if(writePos > 0) {
389 System.arraycopy(oldArray, 0, newArray, 0, writePos);
390 }
391 final int tail = capacity-writePos;
392 if( tail > 0 ) {
393 System.arraycopy(oldArray, writePos, newArray, readPos, tail);
394 }
395
396 capacity = newCapacity;
397 array = newArray;
398 }
399 }
400
401 @SuppressWarnings("unchecked")
402 private static <T> T[] newArray(final Class<? extends T[]> arrayType, final int length) {
403 return ((Object)arrayType == (Object)Object[].class)
404 ? (T[]) new Object[length]
405 : (T[]) Array.newInstance(arrayType.getComponentType(), length);
406 }
407}
Simple synchronized implementation of Ringbuffer.
final boolean put(final T e)
Enqueues the given element.Returns true if successful, otherwise false in case buffer is full....
final boolean putSame(final boolean blocking)
Enqueues the same element at it's write position, if not full.Returns true if successful,...
final void waitForFreeSlots(final int count)
Blocks until at least count free slots become available.
final void growEmptyBuffer(final T[] newElements)
Grows an empty ring buffer, increasing it's capacity about the amount.
final void putBlocking(final T e)
Enqueues the given element.Method blocks until a free slot becomes available via get.
final int size()
Returns the number of elements in this ring buffer.
final int capacity()
Returns the net capacity of this ring buffer.
final T peekBlocking()
Peeks the next element at the read position w/o modifying pointer, but w/ blocking.
final String toString()
Returns a short string representation incl.
final void clear()
Resets the read and write position according to an empty ring buffer and set all ring buffer slots to...
final boolean isFull()
Returns true if this ring buffer is full, otherwise false.
final int getFreeSlots()
Returns the number of free slots available to put.
final void dump(final PrintStream stream, final String prefix)
Debug functionality - Dumps the contents of the internal array.
final void growFullBuffer(final int growAmount)
Grows a full ring buffer, increasing it's capacity about the amount.
final T peek()
Peeks the next element at the read position w/o modifying pointer, nor blocking.
final T getBlocking()
Dequeues the oldest enqueued element.The returned ring buffer slot will be set to null to release the...
final boolean isEmpty()
Returns true if this ring buffer is empty, otherwise false.
SyncedRingbuffer(final Class<? extends T[]> arrayType, final int capacity)
Create an empty ring buffer instance w/ the given net capacity.
final void resetFull(final T[] copyFrom)
Resets the read and write position according to a full ring buffer and fill all slots w/ elements of ...
Ring buffer interface, a.k.a circular buffer.
Definition: Ringbuffer.java:45