/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Sun designates this particular file as subject to the "Classpath" * exception as provided by Sun in the LICENSE file that accompanied * this code. * * -- */ package com.sun.sgs.app.util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Deque; import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Set; import com.sun.sgs.app.AppContext; import com.sun.sgs.app.DataManager; import com.sun.sgs.app.ManagedObject; import com.sun.sgs.app.ManagedObjectRemoval; import com.sun.sgs.app.ManagedReference; import com.sun.sgs.app.ObjectNotFoundException; import com.sun.sgs.app.Task; /** * A scalable {@code Deque} implementation. This implementation * supports concurrent writers at each of the deque's ends. The * internal representation of the deque is segmented so that * operations do not access the data of entire deque. Note that * concurrent read and write operations on the <i>same</i> end of the * deque will still cause contention. * * <p> * * Developers should use this as a drop-in replaced for any {@code * Deque} or {@link Queue} implementation * if the size of the data * structure will be more than a small number of elements, or if the * data structure needs to support concurrent writers for putting and * removing. A standard {@code Deque} or {@code Queue} implementation * will likely perform better than an instance of this class if the * number of elements is small, or if the usage instance happens * entirely during the lifetime of a task and the instance is never * persisted. * * <p> * * The iterator will only make a best-effort to reflect any * concurrent change to the deque. If the next element that the * iterator is to return was been removed during a separate task, the * iterator <i>will</i> throw a {@code * ConcurrentModificationException}. In this behavior, not supporting * concurrent updates incurs no performance overhead for mutating * operations. Furthermore, multiple iterators will not cause any * addtional contention. * * <p> * * Most operations run in constant time. However, there are several * key differences in operation cost between this class and other * {@code Deque} implementations. The operations {@link * ScalableDeque#remove(Object) remove}, {@link * ScalableDeque#removeAll(Collection) removeAll}, {@link * ScalableDeque#removeFirstOccurrence(Object) removeFirstOccurrence}, * and {@link ScalableDeque#removeLastOccurrence(Object) * removeLastOccurrence} run in time linear to the number of instances * of that object in the deque, not the number of elements in the * deque. In addition, the {@link ScalableDeque#contains(Object) * contains} operation is constant time. Due to the distributed nature * of the deque, the {@link ScalableDeque#size() size} operation takes * time linear to the size of the deque. For large deques, this * method should not be called, as it would take longer than the time * available to a {@code Task}. Similarly, the {@link * ScalableDeque#toArray() toArray} and {@link * ScalableDeque#toArray(Object[]) toArray(T[])} operations should not * be used for anything but small deques. The {@link * ScalableDeque#isEmpty() isEmpty} and {@link ScalableDeque#clear() * clear} operations however are still constant time. * * <p> * * All elements stored by this deque must be instances of {@link * Serializable}. This class additionally supports elements * that are instances of {@code ManagedObject}. If a {@code * ManagedObject} is stored within this deque, the developer is still * responsible for removing it from the data store at the end of its * lifetime. Removing a {@code ManagedObject} from the deque by any * operation, including {@code clear}, <i>will not</i> remove that * object from the data store. These objects should be removed from * the data store after they have been removed from the deque. * * <p> * * This class requires that all elements have a meaningful {@code * hashCode} and {@code equals} operation. Failure to do so will * result in the {@code contains}, {@code remove}, {@code * removeFirstOccurrrence}, {@code removeLastOccurrence} and {@code * removeAll} operations not working. The remaining operations will * still operate as normal, however. * * <p> * * This class will mark itself for update as * necessary; no additional calls to the {@link DataManager} are * necessary when modifying the deque. Developers should not need to * call {@code markForUpdate} or {@code getForUpdate} on a deque * instance, as this will eliminate all the concurrency benefits of * this class. However, calling {@code getForUpdate} or {@code * markForUpdate} can be used if an operation needs to prevent all * access to the map. * * <p> * * Since the deque is capable of containing many elements, applications which * use iterators to traverse elements should be aware that prolonged iterative * tasks have the potential to lock out other concurrent tasks on the deque. * Therefore, it is highly recommended (and considered good practice) that * potentially long iterations be broken up into smaller tasks. This strategy * improves the concurrency of the deque as it reduces the * locked elements owned by any one task. Iterators belonging to the deque * implement the {@code java.io.Serializable} interface, but not * {@code com.sun.sgs.app.ManagedObject}. Therefore, to persist them in the * data manager, it is necessary to wrap them within another * {@code ManagedObject}. If the iterators are persisted, the * {@code DataManager.markForUpdate()} (with the wrapper as the argument) * should be called when performing iterator operations like {@code next()}, * {@code remove()}, etc, so that the iterator's state is flushed to the * data manager. * *<p> * * This class and its iterator support all the optional {@link * Collection} and {@code Iterator} operations. This class does not * support {@code null} elements. * * @param <E> the type of elements held by this deque * * @see Deque * @see ManagedObject * @see Serializable */ public class ScalableDeque<E> extends AbstractCollection<E> implements Deque<E>, Serializable, ManagedObjectRemoval { /* * IMPLEMENTATION NOTES: * * The deque is implemented as a doubly-linked list of Element * objects, which are stored in a ScalableHashMap. We use a * ScalableHashMap instead of a ScalableHashSet, as the Set * implementations is wrapper around Map (just like this class), and * therefore would cause an extra data access penalty. Currently, the * value stored in the Map is not meaningful. * * A deque may have multiple elements with the same value. Since a * Map is used to store the Element instances, we have to impose an * additional factor to ensure each Element's uniqueness. Thefefore * each Element has an associated Id based on when it was inserted * into the deque. * * Ids are generated by keeping two counters, one for the head of the * list and one for the tail. The head counter is decremented when an * item is added to the head of the list, whilst the tail counter is * incremented. This invariant ensures a total monotonic ordering of * Ids in the deque. Furthermore, these counters do not impose any * additional contention, since only one is updated depending on which * end of the deque the element is inserted. * * The Element Ids allow this class to support the removal operations * defined in the Deque interface. For example, removeFirst(Object) * would look for all elements that have that same value as Object and * choose the one with the lowest Id. * * In general, a deque need not support random access to the elements * other than the head and tail. However, to properly support the * removal operations, we cannot traverse the entire map's keyset to * find all the elements with the provided value. Therefore, we use * an indexing technique to locate the Elements based on using a * substitute class, ElementMatcher that is equivalent to an Element * that is being searched for. * * Random access to the elements occurs as follows. Each Element the * of map uses the hashCode of the value that it stores. Multiple * Element instances with the same value will still be unique by way * of their Ids. The Map.Entry instances that contain the Element can * be obtained from the backing map using the package-private * ScalableMap.getEntry(Object key) method. * * To locate an Element, we use the ScalableDeque-internal class * ElementMatcher as the key for getEntry(). The ElementMatcher * constructor takes in the value of the Element that we are trying to * find and the class's hashCode method returns the hashCode of that * value. This enables us to find all the Element instances with that * value's hashCode in the backing map. When the map is comparing the * ElementMatcher instance to the Element instances (by doing its * key-comparisons), the ElementMatcher will return true if the Element * has the same value as the one stored in the ElementMatcher. This * basic behavior is all that is necessary to support * Deque.contains(Object) * * To support the removal operations, we impose one additional * complexity, by allowing ElementMatcher instances to filter their * comparisons based on the Id of the Element. For example, an * ElementMatcher would not return true for equals() when compared to * an Element that had an Id it had already seen. This behavior lets * us find *all* the Element instances with the provided Id. Since * the Ids are monotonically ordered, we can then do Id comparisons to * determine the first and last occurrences for the respective remove * operations. */ /** * The version of the serialized form. */ private static final long serialVersionUID = 1; /** * A reference to the element at the head of the list. */ private final ManagedReference<ManagedSerializable<ManagedReference<Element<E>>>> headElement; /** * A counter for the unique Id assigned to elements at the head of * the deque. When a new head element is added, this counter is * decremented for the next element. In doing so, this ensures * that a monotonic ordering exists for all elements, where the * element at the head of the deque has a numerically smaller id * than all other elements. * * @see ScalableDeque#addToHead(Element) */ private final ManagedReference<ManagedSerializable<Long>> headCounter; /** * A reference to the element at the tail end of the list. */ private final ManagedReference<ManagedSerializable<ManagedReference<Element<E>>>> tailElement; /** * A counter for the unique Id assigned to elements at the tail * end of the deque. When a new tail element is added, this * counter is incremented for the next element. In doing so, this * ensures that a monotonic ordering exists for all elements, * where the element at the tail end of the list has a numerically * larger id than all other elements. * * @see ScalableDeque#addToTail(Element) */ private final ManagedReference<ManagedSerializable<Long>> tailCounter; /** * A reference to the {@code ScalableHashMap} that will store all * the mappings */ private final ManagedReference<ScalableHashMap<Element<E>, Long>> backingMapRef; /** * A transient Java reference to the {@code ScalableHashMap} * refered to by {@link #backingMapRef}. This field is set by a * task's first access to the backing map. * * @see ScalableDeque#map() */ private transient ScalableHashMap<Element<E>, Long> backingMap; /** * Creates a new empty {@code ScalableDeque}. * Users of this constructor should refer to the class javadoc * regarding the structure's performance. */ public ScalableDeque() { backingMap = new ScalableHashMap<Element<E>, Long>(); DataManager dm = AppContext.getDataManager(); backingMapRef = dm.createReference(backingMap); // initialize the pointers to the front and end of the deque // to null. However, the reference to these pointers will // always be non-null ManagedSerializable<ManagedReference<Element<E>>> head = new ManagedSerializable<ManagedReference<Element<E>>>(null); ManagedSerializable<Long> headCount = new ManagedSerializable<Long>(-1L); ManagedSerializable<ManagedReference<Element<E>>> tail = new ManagedSerializable<ManagedReference<Element<E>>>(null); ManagedSerializable<Long> tailCount = new ManagedSerializable<Long>(0L); headElement = dm.createReference(head); headCounter = dm.createReference(headCount); tailElement = dm.createReference(tail); tailCounter = dm.createReference(tailCount); } /** * Creates a {@code ScalableDeque} and adds all the elements in the * provided collection according to their traversal ordering. * * @param c the collection of elements the deque will initially * contain */ public ScalableDeque(Collection<? extends E> c) { this(); if (c == null) { throw new NullPointerException("The provided collection " + "cannot be null"); } addAll(c); } /** * Returns the {@link ScalableHashMap} used to store entries. * * @return the backing map. */ private ScalableHashMap<Element<E>, Long> map() { if (backingMap == null) { backingMap = backingMapRef.get(); } return backingMap; } /** * {@inheritDoc} */ public boolean add(E e) { if (e == null) { throw new NullPointerException("cannot add null elements"); } // get the counter for the tail and mark this element with the // next value Long tailCount = getAndIncrementTailCounter(); Element<E> element = new Element<E>(e, tailCount); // add the element to the backing map so its persisted map().put(element, tailCount); // then link after the last element addToTail(element); return true; } /** * {@inheritDoc} */ public void addFirst(E e) { if (e == null) { throw new NullPointerException("cannot add null elements"); } // get the counter for the head element and mark this element // with the next value Long headCount = getAndDecrementHeadCounter(); Element<E> element = new Element<E>(e, headCount); // add it to the backing map map().put(element, headCount); // link it before the first element addToHead(element); } /** * {@inheritDoc} */ public void addLast(E e) { add(e); } /** * Adds the provided {@code Element} to the head of the deque * and updates the head reference. * * @param e the new head {@code Element}. */ private void addToHead(Element<E> e) { DataManager dm = AppContext.getDataManager(); // short-circuit case if this element is the only element in // the deque (i.e. the deque was empty) if (headElement.get().get() == null) { ManagedReference<Element<E>> ref = dm.createReference(e); headElement.get().set(ref); tailElement.get().set(ref); return; } Element<E> oldHead = headElement(); headElement.get().set(dm.createReference(e)); e.setPrev(null); e.setNext(oldHead); oldHead.setPrev(e); } /** * Adds the provided {@code Element} to the tail end of the deque * and updates the tail reference. * * @param e the new tail {@code Element}. */ private void addToTail(Element<E> e) { DataManager dm = AppContext.getDataManager(); // short-circuit case if this element is the only element in // the deque (i.e. the deque was empty) if (tailElement.get().get() == null) { ManagedReference<Element<E>> ref = dm.createReference(e); headElement.get().set(ref); tailElement.get().set(ref); return; } Element<E> oldTail = tailElement(); tailElement.get().set(dm.createReference(e)); e.setPrev(oldTail); e.setNext(null); oldTail.setNext(e); } /** * {@inheritDoc} */ public void clear() { map().clear(); ManagedReference<Element<E>> headRef = headElement.get().get(); // if the deque was already empty, we have no clean up to do if (headRef == null) { return; } // otherwise the deque wasn't already empty, so start a task // to remove all the elements starting at the head AppContext.getTaskManager(). scheduleTask(new AsynchronousClearTask<E>(headRef)); // reset the head and tail pointers headElement.get().set(null); tailElement.get().set(null); } /** * {@inheritDoc}. This operation runs in constant time. */ public boolean contains(Object o) { return map().containsKey(new ElementMatcher<E>(o)); } /** * {@inheritDoc} * <p> * The iterator implements the {@code java.io.Serializable} * interface but does not implement the * {@code com.sun.sgs.app.ManagedObject} interface, so * it is not stored in the data manager by default. * <p> * The iterator throws a {@code ConcurrentModificationException} * if either the next element was removed and {@code hasNext()} * was not called before {@code next()}, or if the iterator was * serialized and the next element was removed before a call * to retrieve the next element is made. */ public Iterator<E> descendingIterator() { return new BidirectionalDequeIterator<E>(this, true); } /** * {@inheritDoc} */ public E element() { E e = getFirst(); if (e == null) { throw new NoSuchElementException(); } return e; } /** * {@inheritDoc} */ public boolean equals(Object o) { /* * IMPLEMENTATION NOTE: The Java general contract for hashCode * requires that if two objects are equal, that they have the * same hash code. However, there is not standard hashCode * algorithm for Deque classes. Therefore, we make a * best-effort to at least check for identity-equality within * members of this class. */ if (o == null || !(o instanceof ScalableDeque)) { return false; } ScalableDeque<E> d = uncheckedCast(o); DataManager dm = AppContext.getDataManager(); return dm.createReference(this).equals(dm.createReference(d)); } /** * Returns the next unique identifier for an {@code Element} that * is being added to the head of the deque. * * @return the next unique identifer for the head of the deque. */ private Long getAndDecrementHeadCounter() { ManagedSerializable<Long> headVal = headCounter.getForUpdate(); Long counter = headVal.get(); // decrement it for the next one headVal.set(counter.longValue() - 1); return counter; } /** * Returns the next unique identifier for an {@code Element} that * is being added to the tail of the deque. * * @return the next unique identifer for the tail of the deque. */ private Long getAndIncrementTailCounter() { ManagedSerializable<Long> tailVal = tailCounter.getForUpdate(); Long counter = tailVal.get(); // increment it for the next one tailVal.set(counter.longValue() + 1); return counter; } /** * {@inheritDoc} */ public E getFirst() { Element<E> e = headElement(); if (e == null) { throw new NoSuchElementException(); } return e.getValue(); } /** * {@inheritDoc} */ public E getLast() { Element<E> e = tailElement(); if (e == null) { throw new NoSuchElementException(); } return e.getValue(); } /** * {@inheritDoc} */ public int hashCode() { return AppContext.getDataManager().createReference(this).hashCode(); } /** * Returns the {@code Element} at the front of this deque, or * {@code null} if this deque is empty. * * @return the element at the front of this deque or {@code null} * if the deque is empty */ private Element<E> headElement() { ManagedReference<Element<E>> headRef = headElement.get().get(); return (headRef == null) ? null : headRef.get(); } /** * {@inheritDoc} */ public boolean isEmpty() { return headElement() == null; } /** * {@inheritDoc} * <p> * The iterator implements the {@code java.io.Serializable} * interface but does not implement the * {@code com.sun.sgs.app.ManagedObject} interface, so * it is not stored in the data manager by default. * <p> * The iterator throws a {@code ConcurrentModificationException} * if either the next element was removed and {@code hasNext()} * was not called before {@code next()}, or if the iterator was * serialized and the next element was removed before a call * to retrieve the next element is made. */ public Iterator<E> iterator() { return new BidirectionalDequeIterator<E>(this, false); } /** * {@inheritDoc} * * @return {@code true} */ public boolean offer(E e) { return offerLast(e); } /** * {@inheritDoc} * * @return {@code true} */ public boolean offerFirst(E e) { if (e == null) { throw new NullPointerException("cannot add null elements"); } addFirst(e); return true; } /** * {@inheritDoc} * * @return {@code true} */ public boolean offerLast(E e) { if (e == null) { throw new NullPointerException("cannot add null elements"); } addLast(e); return true; } /** * {@inheritDoc} */ public E peek() { return peekFirst(); } /** * {@inheritDoc} */ public E peekFirst() { Element<E> e = headElement(); return (e == null) ? null : e.getValue(); } /** * {@inheritDoc} */ public E peekLast() { Element<E> e = tailElement(); return (e == null) ? null : e.getValue(); } /** * {@inheritDoc} */ public E poll() { return pollFirst(); } /** * {@inheritDoc} */ public E pollFirst() { return removeElement(headElement()); } /** * {@inheritDoc} */ public E pollLast() { return removeElement(tailElement()); } /** * {@inheritDoc} */ public E pop() { Element<E> head = headElement(); if (head == null) { throw new NoSuchElementException(); } return removeElement(head); } /** * {@inheritDoc} */ public void push(E e) { addFirst(e); } /** * {@inheritDoc} */ public E remove() { E e = removeFirst(); if (e == null) { throw new NoSuchElementException(); } return e; } /** * {@inheritDoc} Note that this implementation takes time * proportinal to the number of instances of {@code o} in the * deque, <i>not</i> the number of elements. * * @param o {@inheritDoc} * * @return {@inheritDoc} */ public boolean remove(Object o) { return removeFirstOccurrence(o); } /** * {@inheritDoc} */ public boolean removeAll(Collection<?> c) { if (c == null) { throw new NullPointerException("the collection cannot be null"); } boolean updated = false; for (Object o : c) { boolean b = removeAllOccurrences(o); if (b) { updated = true; } } return updated; } /** * Removes all occurrences of the provided object from the deque. * Note that this implementation takes time proportinal to the * number of instances of {@code o} in the deque, <i>not</i> the * number of elements. * * @param o the object to to be removed * * @return {@code true} if the deque was modified as a result of * this operation */ public boolean removeAllOccurrences(Object o) { if (o == null) { throw new NullPointerException("this deque does not support " + "null elements"); } // repeatedly use the same matcher to look for elements with // the provided object in the backing map ElementMatcher<E> matcher = new ElementMatcher<E>(o); ScalableHashMap.PrefixEntry<Element<E>, Long> entry = map().getEntry(matcher); Element<E> e = (entry == null) ? null : entry.getKey(); while (e != null) { // each time we find an object, we first remove it from // the map, then we add its id to the matcher and continue // looking removeElement(e); matcher.addId(e.getId()); entry = map().getEntry(matcher); e = (entry == null) ? null : entry.getKey(); } return !(matcher.alreadySeen.isEmpty()); } /** * Removes the provided {@code Element} from the deque and updates * all the deque-internal structures as necesary. This method * will remove the element from the backing map, patch the element * doubly-linked list, and then if necessary, update head and tail * references. * * @param e the {@code Element} to be removed from the deque * * @return the value contained by the removed {@code Element} */ private E removeElement(Element<E> e) { if (e == null) { return null; } E value = e.getValue(); map().remove(e); // remove e from the deque's backing list Element<E> prev = e.prev(); Element<E> next = e.next(); if (prev != null) { prev.setNext(next); } if (next != null) { next.setPrev(prev); } // update the references to the front and back of the list, if // necessary. We rely on the invariants that the previous and // next element references will only be null if an element is // at an end of the list. DataManager dm = AppContext.getDataManager(); if (e.prevElement == null) { headElement.get().set((next == null) ? null : dm.createReference(next)); } if (e.nextElement == null) { tailElement.get().set((prev == null) ? null : dm.createReference(prev)); } AppContext.getDataManager().removeObject(e); return value; } /** * {@inheritDoc} */ public E removeFirst() { Element<E> head = headElement(); if (head == null) { throw new NoSuchElementException(); } return removeElement(head); } /** * {@inheritDoc} Note that this implementation takes time * proportinal to the number of instances of {@code o} in the * deque, <i>not</i> the number of elements. * * @param o {@inheritDoc} * * @return {@inheritDoc} */ public boolean removeFirstOccurrence(Object o) { if (o == null) { throw new NullPointerException("this deque does not support " + "null elements"); } // repeatedly use the same matcher to look for elements with // the provided object in the backing map ElementMatcher<E> matcher = new ElementMatcher<E>(o); ScalableHashMap.PrefixEntry<Element<E>, Long> entry = map().getEntry(matcher); Element<E> e = (entry == null) ? null : entry.getKey(); // this value represents the highest value any element could // have so any element present will be less than this value long leftMost = Long.MAX_VALUE; Element<E> firstOccurrence = null; while (e != null) { // each time we find an element, we check to see if its id // is lower than the left most we've seen so far. If it // is closer to the head, then we mark that as the first // occurrence and keep searching for any additional // elements in the deque that match the provided object. Long id = e.getId(); if (id.longValue() < leftMost) { leftMost = id.longValue(); firstOccurrence = e; } matcher.addId(id); entry = map().getEntry(matcher); e = (entry == null) ? null : entry.getKey(); } // once we've searched the map for all elements matching the // provided object, remove the first occurrence (the one with // the lowest id) if it was found if (firstOccurrence != null) { removeElement(firstOccurrence); return true; } else { return false; } } /** * {@inheritDoc} */ public E removeLast() { Element<E> tail = tailElement(); if (tail == null) { throw new NoSuchElementException(); } return removeElement(tail); } /** * {@inheritDoc} Note that this implementation takes time * proportinal to the number of instances of {@code o} in the * deque, <i>not</i> the number of elements. * * @param o {@inheritDoc} * * @return {@inheritDoc} */ public boolean removeLastOccurrence(Object o) { if (o == null) { throw new NullPointerException("this deque does not support " + "null elements"); } // repeatedly use the same matcher to look for elements with // the provided object in the backing map ElementMatcher<E> matcher = new ElementMatcher<E>(o); ScalableHashMap.PrefixEntry<Element<E>, Long> entry = map().getEntry(matcher); Element<E> e = (entry == null) ? null : entry.getKey(); // this value represents the lowest value any element could // have, so any element present will be greater than this // value long rightMost = Long.MIN_VALUE; Element<E> lastOccurrence = null; while (e != null) { // each time we find an element, we check to see if its id // is higher than the right most we've seen so far. If it // is closer to the tail, then we mark that as the last // occurrence and keep searching for any additional // elements in the deque that match the provided object. Long id = e.getId(); if (id.longValue() > rightMost) { rightMost = id.longValue(); lastOccurrence = e; } matcher.addId(id); entry = map().getEntry(matcher); e = (entry == null) ? null : entry.getKey(); } // once we've searched the map for all elements matching the // provided object, remove the last occurrence (the one with // the highest id) if it was found if (lastOccurrence != null) { removeElement(lastOccurrence); return true; } else { return false; } } /** * Clears the backing map, then removes all the {@code Element} * instances created by this deque. */ public void removingObject() { clear(); // Remove the ManagedSerializable objects as well DataManager dm = AppContext.getDataManager(); dm.removeObject(backingMap); dm.removeObject(headElement.get()); dm.removeObject(headCounter.get()); dm.removeObject(tailElement.get()); dm.removeObject(tailCounter.get()); } /** * {@inheritDoc}. This operation runs in time proportinal to the * length of this deque. * * @return the size of this deque */ public int size() { int size = 0; Element<E> n = headElement(); while (n != null) { size++; n = n.next(); } return size; } /** * Returns the {@code Element} at the end of this deque, or * {@code null} if this deque is empty. * * @return the element at the end of this deque or {@code null} * if the deque is empty */ private Element<E> tailElement() { ManagedReference<Element<E>> tailRef = tailElement.get().get(); return (tailRef == null) ? null : tailRef.get(); } /** * Reads in all state for the deque and initializes the transient * {@code backingMap} field to {@code null} */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { // read in all the non-transient state s.defaultReadObject(); // set the state of the transient backing map to null backingMap = null; } /** * A deque-internal class for wrapping elements within the deque. * This class maintains a doubly-linked list of all the elements * in the deque. This class relies on {@code ScalableDeque} to * maintain correct references to to the head and tail of this * list, and also to update it accordingly on additions and * removals. * * <p> * * The value of the elements themselves are refered to by {@code * ManagedReference} instances if they implement {@code * ManagedObject} or are refered to using a standard Java * reference. * * <p> * * {@code Element} instances use their value's hash code, but can * be distinguished by their {@code id}, which corresponds to the * id assigned to them at the time of their addition to the deque. * This method of hashing has the effect that when an {@code * Element} is added to the deque, and subsequently added to the * backing map, the deque can locate all instances of the element * by using the element's hash code. This behavior is required to * correctly support the random access behavior of {@link * ScalableDeque#remove(Object) remove}, {@link * ScalableDeque#removeFirstOccurrence(Object) * removeFirstOccurrence}, {@link * ScalableDeque#removeLastOccurrence(Object) * removeLastOccurrence}, and {@link * ScalableDeque#removeAll(Collection) removeAll}. * * @see ScalableDeque$ElementMatcher * * @param <E> The type of element held by the deque */ static class Element<E> implements Serializable, ManagedObject { /** * {@inheritDoc} */ private static final long serialVersionUID = 1L; /** * A {@code ManagedReference} to value of this element if the * elemented implements {@code ManagedObject}, or {@code null} * otherwise. The value of this variable is conditionally set * during object deserialization based on {@code useRef} * * @serial */ private transient ManagedReference<E> valueRef; /** * A Java reference to the value of this element if the * element does not implement {@code ManagedObject}, , or * {@code null} otherwise. The value of this variable is * conditionally set during object deserialization based on * {@code sueRef} * * @serial */ private transient E value; /** * The flag that determines whether the value contained by * this element should be accessed by a {@code * ManagedReference} or a Java reference. */ private final boolean useRef; /** * The Id assigned to this {@code Element} at the time of its * creation. {@code Element} instances are numerically * ordered where the head element will have the numerically * smallest Id, and the tail element will have the numerically * largest Id. * * @see ScalableDeque#add(Object) * @see ScalableDeque#addFirst(Object) */ private final Long id; /** * A reference to the previous {@code Element} in the deque, * or {@code null} if this element is the head of the deque. */ private ManagedReference<Element<E>> prevElement; /** * A reference to the next {@code Element} in the deque, * or {@code null} if this element is the tail of the deque. */ private ManagedReference<Element<E>> nextElement; /** * Constructs a new {@code Element} with the provided {@code * id} to contain the value. * * @param value the value held by this {@code Element} * @param id the id of this element */ public Element(E value, Long id) { if (value == null) { throw new NullPointerException("cannot create Element with " + "null value"); } if (value instanceof ManagedObject) { valueRef = AppContext.getDataManager().createReference(value); this.value = null; useRef = true; } else { this.value = value; valueRef = null; useRef = false; } this.id = id; prevElement = null; nextElement = null; } /** * Returns {@code true} if {@code o} is an instance of {@code * Element}, contains the same value as this instance and has * the same id. * * @param o {@inheritDoc} * * @return {@code true} if {@code o} is an instance of {@code * Element}, contains the same value as this instance and has * the same id. * * @see ScalableDeque$ElementMatcher */ public boolean equals(Object o) { if (o == null) { return false; } else if (o instanceof Element) { Element<E> e = uncheckedCast(o); E v1 = e.getValue(); E v2 = getValue(); return (v1 == v2 || (v1 != null && v1.equals(v2))) && e.id.equals(id); } else if (o instanceof ElementMatcher) { // This case occurs when an ElementMatcher is used by the // ScalableDeque to locate an element in the backing map. // In this case, we use the ElementMatcher's equal // function as it contains more state about the particular // element for which the deque is looking. ElementMatcher<E> matcher = uncheckedCast(o); return matcher.equals(this); } return false; } /** * Returns the hash code provided by the value contained by * this element. * * @return the hash code of the value contained by this * element */ public int hashCode() { E e = getValue(); return e.hashCode(); } /** * Returns the unique id of this {@code Element}. * * @return the id of this instance */ long getId() { return id; } /** * Returns the value contained by this element. * * @return the value contained by this element */ public E getValue() { return (useRef) ? valueRef.get() : value; } /** * Returns the {@code Element} after this instance in the * deque, or {@code null} if this element is the tail of the * deque. * * @return the {@code Element} after this instance in the * deque, or {@code null} if this element is the tail * of the deque. */ Element<E> next() { return (nextElement == null) ? null : nextElement.get(); } /** * Returns the {@code Element} before this instance in the * deque, or {@code null} if this element is the head of the * deque. * * @return the {@code Element} before this instance in the * deque, or {@code null} if this element is the head * of the deque. */ Element<E> prev() { return (prevElement == null) ? null : prevElement.get(); } /** * Sets the link from this {@code Element} to the next * {@code Element} in the deque to {@code next}. * * @code prev the {@code Element} after this {@code Element} * in the deque */ void setNext(Element<E> next) { DataManager dm = AppContext.getDataManager(); ManagedReference<Element<E>> ref = (next == null) ? null : dm.createReference(next); dm.markForUpdate(this); nextElement = ref; } /** * Sets the link from this {@code Element} to the previous * {@code Element} in the deque to {@code prev}. * * @code prev the {@code Element} before this {@code Element} * in the deque */ void setPrev(Element<E> prev) { DataManager dm = AppContext.getDataManager(); ManagedReference<Element<E>> ref = (prev == null) ? null : dm.createReference(prev); dm.markForUpdate(this); prevElement = ref; } /** * {@inheritDoc} */ public String toString() { return getValue() + "~(" + id + ")"; } /** * Writes out all non-transient state and then conditionally * writes either {@code valueRef} or {@code value} depending * on whether this element is supposed to use a {@code * ManagedReference} to access its value. * * @param s {@inheritDoc} */ private void writeObject(ObjectOutputStream s) throws IOException { // write out all the non-transient state s.defaultWriteObject(); // conditionally write either the ManagedReference to the // value or the value itself, if it did not implement // ManagedObject s.writeObject((useRef) ? valueRef : value); } /** * Reconstructs the {@code Element} and initializes {@code * valueRef} or {@code value} depending on whether this * element is supposed to use a {@code ManagedReference} to * access its value. * * @param s {@inheritDoc} */ @SuppressWarnings("unchecked") private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { // read in all the non-transient state s.defaultReadObject(); if (useRef) { valueRef = uncheckedCast(s.readObject()); } else { // note that this is the line with the unchecked cast value = (E) (s.readObject()); } } } /** * A utility class for locating {@code Element} instances in the * backing map. This class hashes to the same value as the {@code * toFind} object provided to the constructor. This class uses an * implementation of {@code hashCode} and {@code equals} that will * return true if an {@code Element} is found that has the same * {@code hashCode}, {@code toFind} is equal to the value of the * {@code Element}, and where the Id of the {@code Element} has * not already been seen. This allows us to use a {@code * ElementMatcher} that wraps an object with {@code * ScalableHashMap#getEntry(Object)} to return the actual {@code * Element} contained within the map. This functionality is * required to support the random access required for {@link * ScalableDeque#remove(Object) remove}, {@link * ScalableDeque#removeFirstOccurrence(Object) * removeFirstOccurrence}, {@link * ScalableDeque#removeLastOccurrence(Object) * removeLastOccurrence}, and {@link * ScalableDeque#removeAll(Collection) removeAll}. * * <p> * * This class also supports repeated use in searching for elements * by way of its {@code alreadySeen} field. A single {@code * ElementMatcher} instance maybe be updated with the {@code * Element} Ids already seen from past searches. For example, * this allows us to search for all {@code Element} instances with * a certain value. * * @see ScalableDeque$Element * * @param <T> the type of element to be matched */ /* * IMPLEMENTATION NOTE: this class does not implement Serializable * to ensure that it can never be accidentally stored in the * backing map. (Or that if it was, an error would occur such * that the programmer would know) */ private static class ElementMatcher<T> { /** * The set of {@code Element} Id values already seen. This * set is empty on construction. */ private Set<Long> alreadySeen; /** * The value of the {@code Element} that this matcher should * find. */ private Object toFind; /** * Constructs an {@code ElementMatcher} to match all {@code * Element} instances that have the same value as {@code * toFind}. * * @param toFind the value of the {@code Element} that this * instance should match */ ElementMatcher(Object toFind) { this.toFind = toFind; alreadySeen = new HashSet<Long>(); } /** * Adds the provided {@code ElementId} to the set of ids that * have already been seen, which has the effect this instances * will no longer match any {@code Element} instances with * the provided id. * * @param id an {@code Element} id that has already been seen */ public void addId(Long id) { alreadySeen.add(id); } /** * Returns {@true} if {@code o} is an instance of {@code * Element}, the element has the same value as the {@code * toFind} value provided to this instance, and the Id of the * {@code Element} is not in the {@code alreadySeen} set. * * @param o {@code inheritDoc} * * @return {@code true} if {@code o} is an {@code Element} * that this instance should match */ public boolean equals(Object o) { if (o == null) { return false; } else if (o instanceof ElementMatcher) { ElementMatcher<T> m = uncheckedCast(o); return (m.equals(toFind)) && alreadySeen.equals(m.alreadySeen); } else if (o instanceof Element) { Element<T> e = uncheckedCast(o); T value = e.getValue(); return (value == toFind || (value != null && value.equals(toFind))) && !alreadySeen.contains(e.getId()); } return false; } /** * Returns the hash code of the value provided as {@code * toFind}. * * @return {@inheritDoc} */ public int hashCode() { return (toFind == null) ? 0 : toFind.hashCode(); } } /** * A implementation of the iterator for * the {@code ScalableDeque} that allows element traverse from * head-to-tail or tail-to-head as required. * * <p> * * If an iterator is created for an empty deque, and then * serialized, it will remain valid upon any subsequent * deserialization. An iterator in this state, where it has been * created but {@code next} has never been called, will always * begin an the first entry in the map, if any, since its * deserialization. * * @param <E> the type of elements returned by the iterator */ static class BidirectionalDequeIterator<E> implements Iterator<E>, Serializable { /** * The version of the serialized form. */ private static final long serialVersionUID = 3; /** * Whether the current entry has already been removed */ private boolean currentRemoved; /** * A reference to the next entry that this iterator will * return */ private ManagedReference<Element<E>> nextElement; /** * A reference to the current entry */ // We need this reference to support removal immediately after // serialization. private ManagedReference<Element<E>> curElement; /** * A reference to the backing deque */ private final ManagedReference<ScalableDeque<E>> dequeRef; /** * {@code true} if this iterator was created with an empty * deque. In this case the iterator will remain at the head * of the head and valid until {@code next} is called. */ private boolean nextElementWasNullOnCreation; /** * {@code true} if this iterator has just been deserialized * and needs to recheck whether its next element is still * valid. * * @see #checkForNextElementUpdates() */ private transient boolean recheckNextElement; /** * Whether this iterator is traversing the deque in reverse */ private final boolean isReverse; /** * Constructs a new {@code BidirectionalDequeIterator}. * * @param deque the deque that will be iterated over * @param isReverse whether this iterator should traverse the * provided deque from tail to head */ BidirectionalDequeIterator(ScalableDeque<E> deque, boolean isReverse) { currentRemoved = false; curElement = null; this.isReverse = isReverse; nextElement = null; nextElementWasNullOnCreation = true; dequeRef = AppContext.getDataManager().createReference(deque); checkForNextElementUpdates(); } /** * {@inheritDoc} */ public boolean hasNext() { if (recheckNextElement) { checkForNextElementUpdates(); } return nextElement != null; } /** * After deserialization, this iterator should check that its * reference to the next element is still valid. * * <p> * * If this iterator was created based on an empty deque, * and has never iterated over the first element, the iterator * must check whether any new elements exist in the deque. Once * an element exists, the iterator updates its nextElement * reference and is no longer in the "empty map" state. * * <p> * * Otherwise, if the iterator was serialized and the next element * was removed, the deque will throw a * {@code ConcurrentModificationException} during a call * to retrieve the next element. * * @see ScalableDeque#checkIterators(Element) */ private void checkForNextElementUpdates() { // check to see if this iterator was created with a null // next element. This flag will only be true if this // iterator has never seen a next element if (nextElementWasNullOnCreation) { if (isReverse) { // see if the last element in the deque is now // non-null Element<E> tail = dequeRef.get().tailElement(); nextElement = (tail == null) ? null : AppContext.getDataManager().createReference(tail); } else { // see if the first element in the deque is now // non-null Element<E> head = dequeRef.get().headElement(); nextElement = (head == null) ? null : AppContext.getDataManager().createReference(head); } // mark if the next element is now non-null. If so, if // we unset the flag and the iterator, which will // never be set to true again. if (nextElement != null) { nextElementWasNullOnCreation = false; } } recheckNextElement = false; } /** * Returns the next element in the {@code ScalableDeque}. This * method will throw a {@link * java.util.ConcurrentModificationException} if the next * element that the iterator was set to return has been * removed from the deque, and {@code hasNext()} as not been * called first. * * @return the next element in the {@code ScalableDeque} * * @throws NoSuchElementException if no further entries exist * @throws ConcurrentModificationException if the next element * of this iterator has been removed from the deque * and {@code hasNext()} had not been called prior to * this method. */ public E next() { if (!hasNext()) { throw new NoSuchElementException(); } Element<E> element = null; try { element = nextElement.get(); } catch (ObjectNotFoundException onfe) { throw new ConcurrentModificationException( "next element was removed from the deque: " + nextElement); } currentRemoved = false; // update the iterator state curElement = nextElement; nextElement = (isReverse) ? element.prevElement : element.nextElement; return element.getValue(); } /** * {@inheritDoc} */ public void remove() { if (currentRemoved) { throw new IllegalStateException( "The current element has already been removed"); } else if (curElement == null) { throw new IllegalStateException("No current element"); } try { dequeRef.get().removeElement(curElement.get()); } catch (ObjectNotFoundException onfe) { // this happens if the value of the current element // was removed from the datastore while this iterator // was serialized. (The element itself is still // present.) We could check for this upon // deserialization, but instead we rely on this lazy // check at call-time here to avoid doing any // unnecessary work. } currentRemoved = true; } /** * {@inheritDoc} */ private void writeObject(ObjectOutputStream s) throws IOException { // write out all the non-transient state s.defaultWriteObject(); } /** * Reconstructs the {@code BidirectionalDequeIterator} from * the provided stream and marks that this iterator should * check that its next element is still valid * * @see BidirectionalDequeIterator#checkForNextElementUpdates() */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { // read in all the non-transient state s.defaultReadObject(); // mark that the iterator should recheck what its next // element is prior to returning any next element. recheckNextElement = true; } } /** * A helper task that will remove the {@code ManagedObject} node * instances of the dequeue after a {@code clear} operation has * been performed. * * @see ScalableDeque#clear() * @see ScalableDeque#removingObject() * * @param <E> the type of elements being removed. */ private static class AsynchronousClearTask<E> implements ManagedObject, Serializable, Task { /** * {@inheritDoc} */ private static final long serialVersionUID = 1L; /** * A reference to the node that should be next removed */ private ManagedReference<Element<E>> curElement; /** * Constructs the task with the first node in the list of * entries to be removed * * @param headElement the element at the head of the deque at * the time of the {@code clear} operation */ public AsynchronousClearTask(ManagedReference<Element<E>> headElement) { this.curElement = headElement; } /** * Clears some entries and re-enqueues this task * if more entries remain. */ public void run() { while (AppContext.getTaskManager().shouldContinue() && curElement != null) { // remove the current node Element<E> e = curElement.get(); AppContext.getDataManager().removeObject(e); curElement = e.nextElement; } // if there are still have more nodes to clean up, then // re-enqueue this task if (curElement != null) { // mark that the task has updated its state AppContext.getDataManager().markForUpdate(this); AppContext.getTaskManager().scheduleTask(this); } else { // otherwise, this has has finished, so remove it from the // data store AppContext.getDataManager().removeObject(this); } } } @SuppressWarnings("unchecked") private static <T> T uncheckedCast(Object object) { return (T) object; } }