/* * Copyright (c) 2016, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 METRON, INC. 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. */ package com.metsci.glimpse.dnc.util; import static com.google.common.base.Objects.equal; import static java.util.Objects.requireNonNull; import java.lang.reflect.Array; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SingletonEvictingBlockingQueue<V> implements BlockingQueue<V> { protected final Lock lock; protected final Condition hasValue; protected V value; public SingletonEvictingBlockingQueue( ) { this.lock = new ReentrantLock( ); this.hasValue = lock.newCondition( ); this.value = null; } @Override public boolean add( V v ) { return setValueUninterruptibly( v ); } @Override public boolean addAll( Collection<? extends V> c ) { if ( c.isEmpty( ) ) { return false; } else { V vLast = null; for ( V v : c ) { requireNonNull( v ); vLast = v; } return add( vLast ); } } @Override public boolean offer( V v ) { setValueUninterruptibly( v ); // Return value is different than add -- indicates whether // the item was successfully added to the queue return true; } @Override public boolean offer( V v, long timeout, TimeUnit unit ) throws InterruptedException { setValueInterruptibly( v ); // Return value is different than add -- indicates whether // the item was successfully added to the queue return true; } @Override public void put( V v ) throws InterruptedException { setValueInterruptibly( v ); } protected boolean setValueUninterruptibly( V v ) { requireNonNull( v ); lock.lock( ); try { if ( equal( v, value ) ) { return false; } else { value = v; hasValue.signalAll( ); return true; } } finally { lock.unlock( ); } } protected boolean setValueInterruptibly( V v ) throws InterruptedException { requireNonNull( v ); lock.lockInterruptibly( ); try { if ( equal( v, value ) ) { return false; } else { value = v; hasValue.signalAll( ); return true; } } finally { lock.unlock( ); } } @Override public V remove( ) { lock.lock( ); try { if ( value == null ) { throw new NoSuchElementException( ); } else { V v = value; value = null; return v; } } finally { lock.unlock( ); } } @Override public V poll( ) { lock.lock( ); try { V v = value; value = null; return v; } finally { lock.unlock( ); } } @Override public V poll( long timeout, TimeUnit unit ) throws InterruptedException { long nanosToWait = unit.toNanos( timeout ); lock.lockInterruptibly( ); try { while ( value == null ) { if ( nanosToWait <= 0 ) { return null; } nanosToWait = hasValue.awaitNanos( nanosToWait ); } V v = value; value = null; return v; } finally { lock.unlock( ); } } @Override public V take( ) throws InterruptedException { lock.lockInterruptibly( ); try { while ( value == null ) { hasValue.await( ); } V v = value; value = null; return v; } finally { lock.unlock( ); } } @Override public V element( ) { lock.lock( ); try { if ( value == null ) { throw new NoSuchElementException( ); } else { return value; } } finally { lock.unlock( ); } } @Override public V peek( ) { lock.lock( ); try { return value; } finally { lock.unlock( ); } } @Override public void clear( ) { lock.lock( ); try { value = null; } finally { lock.unlock( ); } } @Override public int size( ) { lock.lock( ); try { return ( value == null ? 0 : 1 ); } finally { lock.unlock( ); } } @Override public boolean isEmpty( ) { lock.lock( ); try { return ( value == null ); } finally { lock.unlock( ); } } @Override public Iterator<V> iterator( ) { lock.lock( ); try { return new Iterator<V>( ) { V vNext = value; public boolean hasNext( ) { return ( vNext != null ); } public V next( ) { V vCopy = requireNonNull( vNext ); vNext = null; return vCopy; } }; } finally { lock.unlock( ); } } @Override public Object[] toArray( ) { lock.lock( ); try { if ( value == null ) { return new Object[] { }; } else { return new Object[] { value }; } } finally { lock.unlock( ); } } @Override @SuppressWarnings( "unchecked" ) public <T> T[] toArray( T[] a ) { lock.lock( ); try { if ( value == null ) { return ( T[] ) Array.newInstance( a.getClass( ).getComponentType( ), 0 ); } else { T[] array = ( T[] ) Array.newInstance( a.getClass( ).getComponentType( ), 1 ); array[ 0 ] = ( T ) value; return array; } } finally { lock.unlock( ); } } @Override public boolean contains( Object o ) { lock.lock( ); try { return ( value != null && value.equals( o ) ); } finally { lock.unlock( ); } } @Override public boolean containsAll( Collection<?> c ) { lock.lock( ); try { for ( Object o : c ) { boolean contains = ( value != null && value.equals( o ) ); if ( !contains ) { return false; } } return true; } finally { lock.unlock( ); } } @Override public boolean remove( Object o ) { requireNonNull( o ); lock.lock( ); try { if ( value != null && value.equals( o ) ) { value = null; return true; } else { return false; } } finally { lock.unlock( ); } } @Override public boolean removeAll( Collection<?> c ) { lock.lock( ); try { if ( value != null && c.contains( value ) ) { value = null; return true; } else { return false; } } finally { lock.unlock( ); } } @Override public boolean retainAll( Collection<?> c ) { lock.lock( ); try { if ( value != null && !c.contains( value ) ) { value = null; return true; } else { return false; } } finally { lock.unlock( ); } } @Override public int remainingCapacity( ) { return Integer.MAX_VALUE; } @Override public int drainTo( Collection<? super V> c ) { return drainTo( c, 1 ); } @Override public int drainTo( Collection<? super V> c, int maxElements ) { requireNonNull( c ); if ( c == this ) { throw new IllegalArgumentException( ); } if ( maxElements <= 0 ) { return 0; } lock.lock( ); try { if ( value == null ) { return 0; } else { c.add( value ); value = null; return 1; } } finally { lock.unlock( ); } } }