package net.varkhan.core.containers; import net.varkhan.base.containers.*; import net.varkhan.base.containers.Iterable; 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 IndexedContainer implementation</b> * <p/> * This class wraps an unsynchronized IndexedContainer 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 an IndexedContainer 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:01 PM */ public abstract class SyncIndexedContainer<Type> implements IndexedContainer<Type>, Serializable { protected static final long serialVersionUID=1L; protected final Lock rlock; protected final Lock wlock; protected final IndexedContainer<Type> container; protected volatile long opid=0; /********************************************************************************** ** Container constructors **/ /** * Wraps a synchronization layer on top of an IndexedContainer * * @param container the IndexedContainer to synchronize */ public SyncIndexedContainer(IndexedContainer<Type> container) { this.container=container; ReentrantReadWriteLock rwl=new ReentrantReadWriteLock(); this.rlock=rwl.readLock(); this.wlock=rwl.writeLock(); } /** * Wraps an IndexedContainer using predefined synchronization locks * * @param container the IndexedContainer to synchronize * @param rlock the read lock * @param wlock the write lock */ public SyncIndexedContainer(IndexedContainer<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 entries (elements and related indexes) stored in this container */ 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 entry, * {@literal false} otherwise */ public boolean isEmpty() { rlock.lock(); try { return container.isEmpty(); } finally { rlock.unlock(); } } /** * Returns the smallest position higher that any valid index in this container. * * @return the highest valid index plus one */ public long head() { rlock.lock(); try { return container.head(); } finally { rlock.unlock(); } } /********************************************************************************** ** Container entries accessors **/ /** * Indicates whether an index has an associated entry. * * @param index a unique identifier for this entry * * @return {@literal true} if an element is associated with this index, * or {@literal false} if no element is associated with this index */ public boolean has(long index) { rlock.lock(); try { return container.has(index); } finally { rlock.unlock(); } } /** * Extracts the element designated by an index. * * @param index a unique identifier for this entry * * @return the requested element, or {@literal null} if no entry is associated to this index */ public Type get(long index) { rlock.lock(); try { return container.get(index); } finally { rlock.unlock(); } } /********************************************************************************** ** Container entries iterators **/ /** * Iterates over all indexes in the container, using an {@link net.varkhan.base.containers.Index}. * * @return an iterator over all the indexes that designate elements in the container */ public Index indexes() { rlock.lock(); try { return new Index() { final Index iterator=container.indexes(); private long index=-1; private final long exid=opid; public long current() { return index; } public boolean hasNext() { rlock.lock(); try { if(exid!=opid) throw new ConcurrentModificationException(); return iterator.hasNext(); } finally { rlock.unlock(); } } public long next() { rlock.lock(); try { if(exid!=opid) throw new ConcurrentModificationException(); return index=iterator.next(); } finally { rlock.unlock(); } } public boolean hasPrevious() { rlock.lock(); try { if(exid!=opid) throw new ConcurrentModificationException(); return iterator.hasPrevious(); } finally { rlock.unlock(); } } public long previous() { rlock.lock(); try { if(exid!=opid) throw new ConcurrentModificationException(); return index=iterator.previous(); } finally { rlock.unlock(); } } }; } finally { rlock.unlock(); } } /** * Iterates over all indexes in the collection. * * @return an iterable over all the indexes that designate elements in the container */ public java.lang.Iterable<Long> iterateIndexes() { return new java.lang.Iterable<Long>() { public java.util.Iterator<Long> iterator() { rlock.lock(); try { return new java.util.Iterator<Long>() { final java.util.Iterator<Long> iterator=container.iterateIndexes().iterator(); private long exid=opid; public boolean hasNext() { rlock.lock(); try { if(exid!=opid) throw new ConcurrentModificationException(); return iterator.hasNext(); } finally { rlock.unlock(); } } public Long 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(); } } }; } /** * Iterates over all elements in the container. * * @return an iterator 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(); } } /** * Iterate over each element of the container, and pass it as argument to a * visitor's {@link net.varkhan.base.containers.IndexedVisitable.IndexedVisitor#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 IndexedVisitor<Type,Par> vis, Par par) { rlock.lock(); try { return container.visit(new IndexedVisitor<Type,Par>() { private final long exid=opid; public long invoke(long idx, Type obj, Par par) { if(exid!=opid) throw new ConcurrentModificationException(); return vis.invoke(idx, obj, par); } }, par); } finally { rlock.unlock(); } } /** * Iterates over a set of elements designated by an array of indexes. * * @param indexes an array of identifiers * * @return an iterable over all the elements indexed by the identifiers */ public net.varkhan.base.containers.Iterable<? extends Type> iterate(final long[] indexes) { return new Iterable<Type>() { public Iterator<Type> iterator() { rlock.lock(); try { return new Iterator<Type>() { final Iterator<? extends Type> iterator=container.iterate(indexes).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(); } } }; } /** * Iterates over a set of elements designated by an iterator over indexes. * * @param indexes an iterable over identifiers * * @return an iterable over all the elements indexed by the identifiers */ public Iterable<? extends Type> iterate(final java.lang.Iterable<Long> indexes) { return new Iterable<Type>() { public Iterator<Type> iterator() { rlock.lock(); try { return new Iterator<Type>() { final Iterator<? extends Type> iterator=container.iterate(indexes).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(); } } }; } /** * Iterates over a set of elements designated by an iterator over indexes * * @param indexes an iterable over identifiers * * @return an iterable over all the elements indexed by the identifiers */ public Iterable<? extends Type> iterate(final Indexable indexes) { return new Iterable<Type>() { public Iterator<Type> iterator() { rlock.lock(); try { return new Iterator<Type>() { final Iterator<? extends Type> iterator=container.iterate(indexes).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(); } } }; }}