package net.varkhan.core.containers;
import net.varkhan.base.containers.Container;
import net.varkhan.base.containers.Iterator;
import java.io.Serializable;
import java.util.ConcurrentModificationException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* <b>Provide multithread safety over an unsynchronized ontainer implementation</b>
* <p/>
* This class wraps an unsynchronized Container implementation in a read/write
* synchronized layer, provided all read operations on the underlying container are
* state-less (i.e. do not modify any field data in the container).
* <p/>
* Individual read access to container items is thread safe, and iterators are
* fail-fast, meaning they will throw a {@link ConcurrentModificationException}
* whenever is detected a modification on the underlying container that is not
* the result of the iterator's own methods.
* <p/>
* A pair of read/write locks is provided to classes extending this implementation,
* allowing them to protect write operations from concurrent access, and read operations
* from inconsistent state during the writes.
* <p/>
* Note that since a Container is read-only, no specific thread-safety is provided
* by this implementation, unless it is subclassed to wrap writable container types
* using the appropriate locking.
* <p/>
*
* @param <Type> the type of elements in the container
*
* @author varkhan
* @date 2/17/11
* @time 10:36 PM
*/
public abstract class SyncContainer<Type> implements Container<Type>, Serializable {
protected static final long serialVersionUID=1L;
protected final Lock rlock;
protected final Lock wlock;
protected final Container<Type> container;
protected volatile long opid=0;
/**********************************************************************************
** Container constructors
**/
/**
* Wraps a synchronization layer on top of a Container
*
* @param container the Container to synchronize
*/
public SyncContainer(Container<Type> container) {
this.container=container;
ReentrantReadWriteLock rwl=new ReentrantReadWriteLock();
this.rlock=rwl.readLock();
this.wlock=rwl.writeLock();
}
/**
* Wraps a Container using predefined synchronization locks
*
* @param container the Container to synchronize
* @param rlock the read lock
* @param wlock the write lock
*/
public SyncContainer(Container<Type> container, Lock rlock, Lock wlock) {
this.container=container;
this.rlock=rlock;
this.wlock=wlock;
}
/**********************************************************************************
** Container statistics accessors
**/
/**
* Returns the number of elements in this container.
*
* @return the number of elements stored in this list
*/
public long size() {
rlock.lock();
try { return container.size(); }
finally { rlock.unlock(); }
}
/**
* Indicates whether this container is empty.
*
* @return {@literal true} if this container contains no element,
* {@literal false} otherwise
*/
public boolean isEmpty() {
rlock.lock();
try { return container.isEmpty(); }
finally { rlock.unlock(); }
}
/**********************************************************************************
** Container elements iterators
**/
/**
* Iterates over all elements in the container.
*
* @return an iterable over all the elements stored in the container
*/
public Iterator<? extends Type> iterator() {
rlock.lock();
try {
return new Iterator<Type>() {
final Iterator<? extends Type> iterator=container.iterator();
private long exid=opid;
public boolean hasNext() {
rlock.lock();
try {
if(exid!=opid) throw new ConcurrentModificationException();
return iterator.hasNext();
}
finally { rlock.unlock(); }
}
public Type next() {
rlock.lock();
try {
if(exid!=opid) throw new ConcurrentModificationException();
return iterator.next();
}
finally { rlock.unlock(); }
}
public void remove() {
wlock.lock();
try {
if(exid!=opid) throw new ConcurrentModificationException();
exid=++opid;
iterator.remove();
}
finally { wlock.unlock(); }
}
};
}
finally { rlock.unlock(); }
}
/**
* Iterate over each element of the container, and pass it as argument to a
* visitor's {@link Visitor#invoke} method, until this method returns
* a negative count.
*
* @param vis the visitor
* @param par the control parameter
* @param <Par> the type of the control parameter
*
* @return the sum of all positive return values from the visitor
*/
public <Par> long visit(final Visitor<Type,Par> vis, Par par) {
rlock.lock();
try {
return container.visit(new Visitor<Type,Par>() {
private final long exid=opid;
public long invoke(Type obj, Par par) {
if(exid!=opid) throw new ConcurrentModificationException();
return vis.invoke(obj, par);
}
}, par);
}
finally { rlock.unlock(); }
}
}