http://git-wip-us.apache.org/repos/asf/ignite/blob/35706b94/modules/core/src/main/java/org/apache/ignite/internal/util/snaptree/SnapTreeMap.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/snaptree/SnapTreeMap.java b/modules/core/src/main/java/org/apache/ignite/internal/util/snaptree/SnapTreeMap.java deleted file mode 100644 index dce2fb8..0000000 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/snaptree/SnapTreeMap.java +++ /dev/null @@ -1,2917 +0,0 @@ -/* - * Copyright (c) 2009 Stanford University, unless otherwise specified. - * All rights reserved. - * - * This software was developed by the Pervasive Parallelism Laboratory of - * Stanford University, California, USA. - * - * Permission to use, copy, modify, and distribute this software in source - * or binary form for any purpose with or without fee is hereby granted, - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. 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. - * - * 3. Neither the name of Stanford University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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 org.apache.ignite.internal.util.snaptree; - -import org.apache.ignite.*; -import org.apache.ignite.internal.util.*; -import org.apache.ignite.internal.util.lang.*; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.Map; -import java.util.NavigableSet; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.concurrent.ConcurrentNavigableMap; - -// TODO: optimized buildFromSorted -// TODO: submap.clone() - -/** A concurrent AVL tree with fast cloning, based on the algorithm of Bronson, - * Casper, Chafi, and Olukotun, "A Practical Concurrent Binary Search Tree" - * published in PPoPP'10. To simplify the locking protocols rebalancing work - * is performed in pieces, and some removed keys are be retained as routing - * nodes in the tree. - * - * <p>This data structure honors all of the contracts of {@link - * java.util.concurrent.ConcurrentSkipListMap}, with the additional contract - * that clone, size, toArray, and iteration are linearizable (atomic). - * - * <p>The tree uses optimistic concurrency control. No locks are usually - * required for get, containsKey, firstKey, firstEntry, lastKey, or lastEntry. - * Reads are not lock free (or even obstruction free), but obstructing threads - * perform no memory allocation, system calls, or loops, which seems to work - * okay in practice. All of the updates to the tree are performed in fixed- - * size blocks, so restoration of the AVL balance criteria may occur after a - * change to the tree has linearized (but before the mutating operation has - * returned). The tree is always properly balanced when quiescent. - * - * <p>To clone the tree (or produce a snapshot for consistent iteration) the - * root node is marked as shared, which must be (*) done while there are no - * pending mutations. New mutating operations are blocked if a mark is - * pending, and when existing mutating operations are completed the mark is - * made. - * <em>* - It would be less disruptive if we immediately marked the root as - * shared, and then waited for pending operations that might not have seen the - * mark without blocking new mutations. This could result in imbalance being - * frozen into the shared portion of the tree, though. To minimize the - * problem we perform the mark and reenable mutation on whichever thread - * notices that the entry count has become zero, to reduce context switches on - * the critical path.</em> - * - * <p>The same multi-cache line data structure required for efficiently - * tracking the entry and exit for mutating operations is used to maintain the - * current size of the tree. This means that the size can be computed by - * quiescing as for a clone, but without doing any marking. - * - * <p>Range queries such as higherKey are not amenable to the optimistic - * hand-over-hand locking scheme used for exact searches, so they are - * implemented with pessimistic concurrency control. Mutation can be - * considered to acquire a lock on the map in Intention-eXclusive mode, range - * queries, size(), and root marking acquire the lock in Shared mode. - * - * @author Nathan Bronson - */ -@SuppressWarnings("ALL") -public class SnapTreeMap<K, V> extends AbstractMap<K, V> implements ConcurrentNavigableMap<K, V>, Cloneable, - Serializable { - - /** */ - private static final long serialVersionUID = 0L; - - /** If false, null values will trigger a NullPointerException. When false, - * this map acts exactly like a ConcurrentSkipListMap, except for the - * running time of the methods. The ability to get a snapshot reduces the - * potential ambiguity between null values and absent entries, so I'm not - * sure what the default should be. - */ - static final boolean AllowNullValues = false; - - /** This is a special value that indicates the presence of a null value, - * to differentiate from the absence of a value. Only used when - * {@link #AllowNullValues} is true. - */ - static final Object SpecialNull = new Object(); - - /** This is a special value that indicates that an optimistic read - * failed. - */ - static final Object SpecialRetry = new Object(); - - - /** The number of spins before yielding. */ - static final int SpinCount = Integer.parseInt(System.getProperty("snaptree.spin", "100")); - - /** The number of yields before blocking. */ - static final int YieldCount = Integer.parseInt(System.getProperty("snaptree.yield", "0")); - - - // we encode directions as characters - static final char Left = 'L'; - static final char Right = 'R'; - - - /** An <tt>OVL</tt> is a version number and lock used for optimistic - * concurrent control of some program invariant. If {@link #isShrinking} - * then the protected invariant is changing. If two reads of an OVL are - * performed that both see the same non-changing value, the reader may - * conclude that no changes to the protected invariant occurred between - * the two reads. The special value UnlinkedOVL is not changing, and is - * guaranteed to not result from a normal sequence of beginChange and - * endChange operations. - * <p> - * For convenience <tt>endChange(ovl) == endChange(beginChange(ovl))</tt>. - */ - static long beginChange(long ovl) { return ovl | 1; } - static long endChange(long ovl) { return (ovl | 3) + 1; } - static final long UnlinkedOVL = 2; - - static boolean isShrinking(long ovl) { return (ovl & 1) != 0; } - static boolean isUnlinked(long ovl) { return (ovl & 2) != 0; } - static boolean isShrinkingOrUnlinked(long ovl) { return (ovl & 3) != 0L; } - - - protected static class Node<K,V> implements Map.Entry<K,V> { - public K key; - volatile int height; - - /** null means this node is conceptually not present in the map. - * SpecialNull means the value is null. - */ - volatile Object vOpt; - volatile Node<K,V> parent; - volatile long shrinkOVL; - volatile Node<K,V> left; - volatile Node<K,V> right; - - Node(final K key, - final int height, - final Object vOpt, - final Node<K,V> parent, - final long shrinkOVL, - final Node<K,V> left, - final Node<K,V> right) - { - this.key = key; - this.height = height; - this.vOpt = vOpt; - this.parent = parent; - this.shrinkOVL = shrinkOVL; - this.left = left; - this.right = right; - } - - @Override - public K getKey() { return key; } - - @Override - @SuppressWarnings("unchecked") - public V getValue() { - final Object tmp = vOpt; - if (AllowNullValues) { - return tmp == SpecialNull ? null : (V)tmp; - } else { - return (V)tmp; - } - } - - @Override - public V setValue(final V v) { - throw new UnsupportedOperationException(); - } - - Node<K,V> child(char dir) { return dir == Left ? left : right; } - - void setChild(char dir, Node<K,V> node) { - if (dir == Left) { - left = node; - } else { - right = node; - } - } - - //////// copy-on-write stuff - - private static <K,V> boolean isShared(final Node<K,V> node) { - return node != null && node.parent == null; - } - - static <K,V> Node<K,V> markShared(final Node<K,V> node) { - if (node != null) { - node.parent = null; - } - return node; - } - - private Node<K,V> lazyCopy(Node<K,V> newParent) { - assert (isShared(this)); - assert (!isShrinkingOrUnlinked(shrinkOVL)); - - return new Node<K,V>(key, height, vOpt, newParent, 0L, markShared(left), markShared(right)); - } - - Node<K,V> unsharedLeft() { - final Node<K,V> cl = left; - if (!isShared(cl)) { - return cl; - } else { - lazyCopyChildren(); - return left; - } - } - - Node<K,V> unsharedRight() { - final Node<K,V> cr = right; - if (!isShared(cr)) { - return cr; - } else { - lazyCopyChildren(); - return right; - } - } - - Node<K,V> unsharedChild(final char dir) { - return dir == Left ? unsharedLeft() : unsharedRight(); - } - - private synchronized void lazyCopyChildren() { - final Node<K,V> cl = left; - if (isShared(cl)) { - left = cl.lazyCopy(this); - } - final Node<K,V> cr = right; - if (isShared(cr)) { - right = cr.lazyCopy(this); - } - } - - //////// per-node blocking - - private void waitUntilShrinkCompleted(final long ovl) { - if (!isShrinking(ovl)) { - return; - } - - for (int tries = 0; tries < SpinCount; ++tries) { - if (shrinkOVL != ovl) { - return; - } - } - - for (int tries = 0; tries < YieldCount; ++tries) { - Thread.yield(); - if (shrinkOVL != ovl) { - return; - } - } - - // spin and yield failed, use the nuclear option - synchronized (this) { - // we can't have gotten the lock unless the shrink was over - } - assert(shrinkOVL != ovl); - } - - int validatedHeight() { - final int hL = left == null ? 0 : left.validatedHeight(); - final int hR = right == null ? 0 : right.validatedHeight(); - assert(Math.abs(hL - hR) <= 1); - final int h = 1 + Math.max(hL, hR); - assert(h == height); - return height; - } - - //////// SubMap.size() helper - - static <K,V> int computeFrozenSize(Node<K,V> root, - Comparable<? super K> fromCmp, - boolean fromIncl, - final Comparable<? super K> toCmp, - final boolean toIncl) { - int result = 0; - while (true) { - if (root == null) { - return result; - } - if (fromCmp != null) { - final int c = fromCmp.compareTo(root.key); - if (c > 0 || (c == 0 && !fromIncl)) { - // all matching nodes are on the right side - root = root.right; - continue; - } - } - if (toCmp != null) { - final int c = toCmp.compareTo(root.key); - if (c < 0 || (c == 0 && !toIncl)) { - // all matching nodes are on the left side - root = root.left; - continue; - } - } - - // Current node matches. Nodes on left no longer need toCmp, nodes - // on right no longer need fromCmp. - if (root.vOpt != null) { - ++result; - } - result += computeFrozenSize(root.left, fromCmp, fromIncl, null, false); - fromCmp = null; - root = root.right; - } - } - - //////// Map.Entry stuff - - @Override - public boolean equals(final Object o) { - if (!(o instanceof Map.Entry)) { - return false; - } - final Map.Entry rhs = (Map.Entry)o; - return eq(key, rhs.getKey()) && eq(getValue(), rhs.getValue()); - } - - private static boolean eq(final Object o1, final Object o2) { - return o1 == null ? o2 == null : o1.equals(o2); - } - - @Override - public int hashCode() { - return (key == null ? 0 : key.hashCode()) ^ - (getValue() == null ? 0 : getValue().hashCode()); - } - - @Override - public String toString() { - return key + "=" + getValue(); - } - } - - private static class RootHolder<K,V> extends Node<K,V> { - RootHolder() { - super(null, 1, null, null, 0L, null, null); - } - - RootHolder(final RootHolder<K,V> snapshot) { - super(null, 1 + snapshot.height, null, null, 0L, null, snapshot.right); - } - } - - private static class COWMgr<K,V> extends CopyOnWriteManager<RootHolder<K,V>> { - COWMgr() { - super(new RootHolder<K,V>(), 0); - } - - COWMgr(final RootHolder<K,V> initialValue, final int initialSize) { - super(initialValue, initialSize); - } - - protected RootHolder<K,V> freezeAndClone(final RootHolder<K,V> value) { - Node.markShared(value.right); - return new RootHolder<K,V>(value); - } - - protected RootHolder<K,V> cloneFrozen(final RootHolder<K,V> frozenValue) { - return new RootHolder<K,V>(frozenValue); - } - } - - //////// node access functions - - private static int height(final Node<?,?> node) { - return node == null ? 0 : node.height; - } - - @SuppressWarnings("unchecked") - private V decodeNull(final Object vOpt) { - assert (vOpt != SpecialRetry); - if (AllowNullValues) { - return vOpt == SpecialNull ? null : (V)vOpt; - } else { - return (V)vOpt; - } - } - - private static Object encodeNull(final Object v) { - if (AllowNullValues) { - return v == null ? SpecialNull : v; - } else { - if (v == null) { - throw new NullPointerException(); - } - return v; - } - } - - //////////////// state - - private final Comparator<? super K> comparator; - private transient volatile COWMgr<K,V> holderRef; - - //////////////// public interface - - public SnapTreeMap() { - this.comparator = null; - this.holderRef = new COWMgr<K,V>(); - } - - public SnapTreeMap(final Comparator<? super K> comparator) { - this.comparator = comparator; - this.holderRef = new COWMgr<K,V>(); - } - - public SnapTreeMap(final Map<? extends K, ? extends V> source) { - this.comparator = null; - this.holderRef = new COWMgr<K,V>(); - putAll(source); - } - - public SnapTreeMap(final SortedMap<K,? extends V> source) { - this.comparator = source.comparator(); - if (source instanceof SnapTreeMap) { - final SnapTreeMap<K,V> s = (SnapTreeMap<K,V>) source; - this.holderRef = (COWMgr<K,V>) s.holderRef.clone(); - } - else { - // TODO: take advantage of the sort order - // for now we optimize only by bypassing the COWMgr - int size = 0; - final RootHolder<K,V> holder = new RootHolder<K,V>(); - for (Map.Entry<K,? extends V> e : source.entrySet()) { - final K k = e.getKey(); - final V v = e.getValue(); - if (k == null) { - throw new NullPointerException("source map contained a null key"); - } - if (!AllowNullValues && v == null) { - throw new NullPointerException("source map contained a null value"); - } - updateUnderRoot(k, comparable(k), UpdateAlways, null, encodeNull(v), holder); - ++size; - } - - this.holderRef = new COWMgr<K,V>(holder, size); - } - } - - @SuppressWarnings("unchecked") - @Override - public SnapTreeMap<K,V> clone() { - final SnapTreeMap<K,V> copy; - try { - copy = (SnapTreeMap<K,V>) super.clone(); - } catch (final CloneNotSupportedException xx) { - throw new InternalError(); - } - assert(copy.comparator == comparator); - copy.holderRef = (COWMgr<K,V>) holderRef.clone(); - return copy; - } - - @Override - public int size() { - return holderRef.size(); - } - - @Override - public boolean isEmpty() { - // removed-but-not-unlinked nodes cannot be leaves, so if the tree is - // truly empty then the root holder has no right child - return holderRef.read().right == null; - } - - @Override - public void clear() { - holderRef = new COWMgr<K,V>(); - } - - @Override - public Comparator<? super K> comparator() { - return comparator; - } - - @Override - public boolean containsValue(final Object value) { - // apply the same null policy as the rest of the code, but fall - // back to the default implementation - encodeNull(value); - return super.containsValue(value); - } - - //////// concurrent search - - @Override - public boolean containsKey(final Object key) { - return getImpl(key) != null; - } - - @Override - public V get(final Object key) { - return decodeNull(getImpl(key)); - } - - @SuppressWarnings("unchecked") - protected Comparable<? super K> comparable(final Object key) { - if (key == null) { - throw new NullPointerException(); - } - if (comparator == null) { - return (Comparable<? super K>)key; - } - return new Comparable<K>() { - final Comparator<? super K> _cmp = comparator; - - @SuppressWarnings("unchecked") - public int compareTo(final K rhs) { return _cmp.compare((K)key, rhs); } - }; - } - - /** Returns either a value or SpecialNull, if present, or null, if absent. */ - private Object getImpl(final Object key) { - final Comparable<? super K> k = comparable(key); - - while (true) { - final Node<K,V> right = holderRef.read().right; - if (right == null) { - return null; - } else { - final int rightCmp = k.compareTo(right.key); - if (rightCmp == 0) { - // who cares how we got here - return right.vOpt; - } - - final long ovl = right.shrinkOVL; - if (isShrinkingOrUnlinked(ovl)) { - right.waitUntilShrinkCompleted(ovl); - // RETRY - } else if (right == holderRef.read().right) { - // the reread of .right is the one protected by our read of ovl - final Object vo = attemptGet(k, right, (rightCmp < 0 ? Left : Right), ovl); - if (vo != SpecialRetry) { - return vo; - } - // else RETRY - } - } - } - } - - private Object attemptGet(final Comparable<? super K> k, - final Node<K,V> node, - final char dirToC, - final long nodeOVL) { - while (true) { - final Node<K,V> child = node.child(dirToC); - - if (child == null) { - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - - // Note is not present. Read of node.child occurred while - // parent.child was valid, so we were not affected by any - // shrinks. - return null; - } else { - final int childCmp = k.compareTo(child.key); - if (childCmp == 0) { - // how we got here is irrelevant - return child.vOpt; - } - - // child is non-null - final long childOVL = child.shrinkOVL; - if (isShrinkingOrUnlinked(childOVL)) { - child.waitUntilShrinkCompleted(childOVL); - - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - // else RETRY - } else if (child != node.child(dirToC)) { - // this .child is the one that is protected by childOVL - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - // else RETRY - } else { - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - - // At this point we know that the traversal our parent took - // to get to node is still valid. The recursive - // implementation will validate the traversal from node to - // child, so just prior to the nodeOVL validation both - // traversals were definitely okay. This means that we are - // no longer vulnerable to node shrinks, and we don't need - // to validate nodeOVL any more. - final Object vo = attemptGet(k, child, (childCmp < 0 ? Left : Right), childOVL); - if (vo != SpecialRetry) { - return vo; - } - // else RETRY - } - } - } - } - - @Override - public K firstKey() { - return extremeKeyOrThrow(Left); - } - - @Override - @SuppressWarnings("unchecked") - public Map.Entry<K,V> firstEntry() { - return (SimpleImmutableEntry<K,V>) extreme(false, Left); - } - - @Override - public K lastKey() { - return extremeKeyOrThrow(Right); - } - - @SuppressWarnings("unchecked") - public Map.Entry<K,V> lastEntry() { - return (SimpleImmutableEntry<K,V>) extreme(false, Right); - } - - private K extremeKeyOrThrow(final char dir) { - final K k = (K) extreme(true, dir); - if (k == null) { - throw new NoSuchElementException(); - } - return k; - } - - /** Returns a key if returnKey is true, a SimpleImmutableEntry otherwise. - * Returns null if none exists. - */ - private Object extreme(final boolean returnKey, final char dir) { - while (true) { - final Node<K,V> right = holderRef.read().right; - if (right == null) { - return null; - } else { - final long ovl = right.shrinkOVL; - if (isShrinkingOrUnlinked(ovl)) { - right.waitUntilShrinkCompleted(ovl); - // RETRY - } else if (right == holderRef.read().right) { - // the reread of .right is the one protected by our read of ovl - final Object vo = attemptExtreme(returnKey, dir, right, ovl); - if (vo != SpecialRetry) { - return vo; - } - // else RETRY - } - } - } - } - - private Object attemptExtreme(final boolean returnKey, - final char dir, - final Node<K,V> node, - final long nodeOVL) { - while (true) { - final Node<K,V> child = node.child(dir); - - if (child == null) { - // read of the value must be protected by the OVL, because we - // must linearize against another thread that inserts a new min - // key and then changes this key's value - final Object vo = node.vOpt; - - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - - assert(vo != null); - - return returnKey ? node.key : new SimpleImmutableEntry<K,V>(node.key, decodeNull(vo)); - } else { - // child is non-null - final long childOVL = child.shrinkOVL; - if (isShrinkingOrUnlinked(childOVL)) { - child.waitUntilShrinkCompleted(childOVL); - - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - // else RETRY - } else if (child != node.child(dir)) { - // this .child is the one that is protected by childOVL - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - // else RETRY - } else { - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - - final Object vo = attemptExtreme(returnKey, dir, child, childOVL); - if (vo != SpecialRetry) { - return vo; - } - // else RETRY - } - } - } - } - - //////////////// quiesced search - - @Override - @SuppressWarnings("unchecked") - public K lowerKey(final K key) { - return (K) boundedExtreme(null, false, comparable(key), false, true, Right); - } - - @Override - @SuppressWarnings("unchecked") - public K floorKey(final K key) { - return (K) boundedExtreme(null, false, comparable(key), true, true, Right); - } - - @Override - @SuppressWarnings("unchecked") - public K ceilingKey(final K key) { - return (K) boundedExtreme(comparable(key), true, null, false, true, Left); - } - - @Override - @SuppressWarnings("unchecked") - public K higherKey(final K key) { - return (K) boundedExtreme(comparable(key), false, null, false, true, Left); - } - - - @Override - @SuppressWarnings("unchecked") - public Entry<K,V> lowerEntry(final K key) { - return (Entry<K,V>) boundedExtreme(null, false, comparable(key), false, false, Right); - } - - @Override - @SuppressWarnings("unchecked") - public Entry<K,V> floorEntry(final K key) { - return (Entry<K,V>) boundedExtreme(null, false, comparable(key), true, false, Right); - } - - @Override - @SuppressWarnings("unchecked") - public Entry<K,V> ceilingEntry(final K key) { - return (Entry<K,V>) boundedExtreme(comparable(key), true, null, false, false, Left); - } - - @Override - @SuppressWarnings("unchecked") - public Entry<K,V> higherEntry(final K key) { - return (Entry<K,V>) boundedExtreme(comparable(key), false, null, false, false, Left); - } - - /** Returns null if none exists. */ - @SuppressWarnings("unchecked") - private K boundedExtremeKeyOrThrow(final Comparable<? super K> minCmp, - final boolean minIncl, - final Comparable<? super K> maxCmp, - final boolean maxIncl, - final char dir) { - final K k = (K) boundedExtreme(minCmp, minIncl, maxCmp, maxIncl, true, dir); - if (k == null) { - throw new NoSuchElementException(); - } - return k; - } - - /** Returns null if none exists. */ - @SuppressWarnings("unchecked") - private Object boundedExtreme(final Comparable<? super K> minCmp, - final boolean minIncl, - final Comparable<? super K> maxCmp, - final boolean maxIncl, - final boolean returnKey, - final char dir) { - K resultKey; - Object result; - - if ((dir == Left && minCmp == null) || (dir == Right && maxCmp == null)) { - // no bound in the extreme direction, so use the concurrent search - result = extreme(returnKey, dir); - if (result == null) { - return null; - } - resultKey = returnKey ? (K) result : ((SimpleImmutableEntry<K,V>) result).getKey(); - } - else { - RootHolder holder = holderRef.availableFrozen(); - final Epoch.Ticket ticket; - if (holder == null) { - ticket = holderRef.beginQuiescent(); - holder = holderRef.read(); - } - else { - ticket = null; - } - try { - final Node<K,V> node = (dir == Left) - ? boundedMin(holder.right, minCmp, minIncl) - : boundedMax(holder.right, maxCmp, maxIncl); - if (node == null) { - return null; - } - resultKey = node.key; - if (returnKey) { - result = node.key; - } - else if (ticket == null) { - // node of a frozen tree is okay, copy otherwise - result = node; - } - else { - // we must copy the node - result = new SimpleImmutableEntry<K,V>(node.key, node.getValue()); - } - } - finally { - if (ticket != null) { - ticket.leave(0); - } - } - } - - if (dir == Left && maxCmp != null) { - final int c = maxCmp.compareTo(resultKey); - if (c < 0 || (c == 0 && !maxIncl)) { - return null; - } - } - if (dir == Right && minCmp != null) { - final int c = minCmp.compareTo(resultKey); - if (c > 0 || (c == 0 && !minIncl)) { - return null; - } - } - - return result; - } - - private Node<K,V> boundedMin(Node<K,V> node, - final Comparable<? super K> minCmp, - final boolean minIncl) { - while (node != null) { - final int c = minCmp.compareTo(node.key); - if (c < 0) { - // there may be a matching node on the left branch - final Node<K,V> z = boundedMin(node.left, minCmp, minIncl); - if (z != null) { - return z; - } - } - - if (c < 0 || (c == 0 && minIncl)) { - // this node is a candidate, is it actually present? - if (node.vOpt != null) { - return node; - } - } - - // the matching node is on the right branch if it is present - node = node.right; - } - return null; - } - - private Node<K,V> boundedMax(Node<K,V> node, - final Comparable<? super K> maxCmp, - final boolean maxIncl) { - while (node != null) { - final int c = maxCmp.compareTo(node.key); - if (c > 0) { - // there may be a matching node on the right branch - final Node<K,V> z = boundedMax(node.right, maxCmp, maxIncl); - if (z != null) { - return z; - } - } - - if (c > 0 || (c == 0 && maxIncl)) { - // this node is a candidate, is it actually present? - if (node.vOpt != null) { - return node; - } - } - - // the matching node is on the left branch if it is present - node = node.left; - } - return null; - } - - //////////////// update - - private static final int UpdateAlways = 0; - private static final int UpdateIfAbsent = 1; - private static final int UpdateIfPresent = 2; - private static final int UpdateIfEq = 3; - - private static boolean shouldUpdate(final int func, final Object prev, final Object expected) { - switch (func) { - case UpdateAlways: return true; - case UpdateIfAbsent: return prev == null; - case UpdateIfPresent: return prev != null; - default: { // UpdateIfEq - assert(expected != null); - if (prev == null) { - return false; - } - if (AllowNullValues && (prev == SpecialNull || expected == SpecialNull)) { - return prev == SpecialNull && expected == SpecialNull; - } - return prev.equals(expected); - } - } - } - - private static Object noUpdateResult(final int func, final Object prev) { - return func == UpdateIfEq ? Boolean.FALSE : prev; - } - - private static Object updateResult(final int func, final Object prev) { - return func == UpdateIfEq ? Boolean.TRUE : prev; - } - - private static int sizeDelta(final int func, final Object result, final Object newValue) { - switch (func) { - case UpdateAlways: { - return (result != null ? -1 : 0) + (newValue != null ? 1 : 0); - } - case UpdateIfAbsent: { - assert(newValue != null); - return result != null ? 0 : 1; - } - case UpdateIfPresent: { - return result == null ? 0 : (newValue != null ? 0 : -1); - } - default: { // UpdateIfEq - return !((Boolean) result) ? 0 : (newValue != null ? 0 : -1); - } - } - } - - @Override - public V put(final K key, final V value) { - return decodeNull(update(key, UpdateAlways, null, encodeNull(value))); - } - - @Override - public V putIfAbsent(final K key, final V value) { - return decodeNull(update(key, UpdateIfAbsent, null, encodeNull(value))); - } - - @Override - public V replace(final K key, final V value) { - return decodeNull(update(key, UpdateIfPresent, null, encodeNull(value))); - } - - @Override - public boolean replace(final K key, final V oldValue, final V newValue) { - return (Boolean) update(key, UpdateIfEq, encodeNull(oldValue), encodeNull(newValue)); - } - - @Override - public V remove(final Object key) { - return decodeNull(update(key, UpdateAlways, null, null)); - } - - @Override - public boolean remove(final Object key, final Object value) { - if (key == null) { - throw new NullPointerException(); - } - if (!AllowNullValues && value == null) { - return false; - } - return (Boolean) update(key, UpdateIfEq, encodeNull(value), null); - } - - // manages the epoch - private Object update(final Object key, - final int func, - final Object expected, - final Object newValue) { - final Comparable<? super K> k = comparable(key); - int sd = 0; - final Epoch.Ticket ticket = holderRef.beginMutation(); - try { - final Object result = updateUnderRoot(key, k, func, expected, newValue, holderRef.mutable()); - sd = sizeDelta(func, result, newValue); - return result; - } finally { - ticket.leave(sd); - } - } - - // manages updates to the root holder - @SuppressWarnings("unchecked") - private Object updateUnderRoot(final Object key, - final Comparable<? super K> k, - final int func, - final Object expected, - final Object newValue, - final RootHolder<K,V> holder) { - - while (true) { - final Node<K,V> right = holder.unsharedRight(); - if (right == null) { - // key is not present - if (!shouldUpdate(func, null, expected)) { - return noUpdateResult(func, null); - } - if (newValue == null || attemptInsertIntoEmpty((K)key, newValue, holder)) { - // nothing needs to be done, or we were successful, prev value is Absent - return updateResult(func, null); - } - // else RETRY - } else { - final long ovl = right.shrinkOVL; - if (isShrinkingOrUnlinked(ovl)) { - right.waitUntilShrinkCompleted(ovl); - // RETRY - } else if (right == holder.right) { - // this is the protected .right - final Object vo = attemptUpdate(key, k, func, expected, newValue, holder, right, ovl); - if (vo != SpecialRetry) { - return vo; - } - // else RETRY - } - } - } - } - - private boolean attemptInsertIntoEmpty(final K key, - final Object vOpt, - final RootHolder<K,V> holder) { - synchronized (holder) { - if (holder.right == null) { - holder.right = new Node<K,V>(key, 1, vOpt, holder, 0L, null, null); - holder.height = 2; - return true; - } else { - return false; - } - } - } - - /** If successful returns the non-null previous value, SpecialNull for a - * null previous value, or null if not previously in the map. - * The caller should retry if this method returns SpecialRetry. - */ - @SuppressWarnings("unchecked") - private Object attemptUpdate(final Object key, - final Comparable<? super K> k, - final int func, - final Object expected, - final Object newValue, - final Node<K,V> parent, - final Node<K,V> node, - final long nodeOVL) { - // As the search progresses there is an implicit min and max assumed for the - // branch of the tree rooted at node. A left rotation of a node x results in - // the range of keys in the right branch of x being reduced, so if we are at a - // node and we wish to traverse to one of the branches we must make sure that - // the node has not undergone a rotation since arriving from the parent. - // - // A rotation of node can't screw us up once we have traversed to node's - // child, so we don't need to build a huge transaction, just a chain of - // smaller read-only transactions. - - assert (nodeOVL != UnlinkedOVL); - - final int cmp = k.compareTo(node.key); - if (cmp == 0) { - return attemptNodeUpdate(func, expected, newValue, parent, node); - } - - final char dirToC = cmp < 0 ? Left : Right; - - while (true) { - final Node<K,V> child = node.unsharedChild(dirToC); - - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - - if (child == null) { - // key is not present - if (newValue == null) { - // Removal is requested. Read of node.child occurred - // while parent.child was valid, so we were not affected - // by any shrinks. - return noUpdateResult(func, null); - } else { - // Update will be an insert. - final boolean success; - final Node<K,V> damaged; - synchronized (node) { - // Validate that we haven't been affected by past - // rotations. We've got the lock on node, so no future - // rotations can mess with us. - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - - if (node.child(dirToC) != null) { - // Lost a race with a concurrent insert. No need - // to back up to the parent, but we must RETRY in - // the outer loop of this method. - success = false; - damaged = null; - } else { - // We're valid. Does the user still want to - // perform the operation? - if (!shouldUpdate(func, null, expected)) { - return noUpdateResult(func, null); - } - - // Create a new leaf - node.setChild(dirToC, new Node<K,V>((K)key, 1, newValue, node, 0L, null, null)); - success = true; - - // attempt to fix node.height while we've still got - // the lock - damaged = fixHeight_nl(node); - } - } - if (success) { - fixHeightAndRebalance(damaged); - return updateResult(func, null); - } - // else RETRY - } - } else { - // non-null child - final long childOVL = child.shrinkOVL; - if (isShrinkingOrUnlinked(childOVL)) { - child.waitUntilShrinkCompleted(childOVL); - // RETRY - } else if (child != node.child(dirToC)) { - // this second read is important, because it is protected - // by childOVL - // RETRY - } else { - // validate the read that our caller took to get to node - if (node.shrinkOVL != nodeOVL) { - return SpecialRetry; - } - - // At this point we know that the traversal our parent took - // to get to node is still valid. The recursive - // implementation will validate the traversal from node to - // child, so just prior to the nodeOVL validation both - // traversals were definitely okay. This means that we are - // no longer vulnerable to node shrinks, and we don't need - // to validate nodeOVL any more. - final Object vo = attemptUpdate(key, k, func, expected, newValue, node, child, childOVL); - if (vo != SpecialRetry) { - return vo; - } - // else RETRY - } - } - } - } - - /** parent will only be used for unlink, update can proceed even if parent - * is stale. - */ - private Object attemptNodeUpdate(final int func, - final Object expected, - final Object newValue, - final Node<K,V> parent, - final Node<K,V> node) { - if (newValue == null) { - // removal - if (node.vOpt == null) { - // This node is already removed, nothing to do. - return noUpdateResult(func, null); - } - } - - if (newValue == null && (node.left == null || node.right == null)) { - // potential unlink, get ready by locking the parent - final Object prev; - final Node<K,V> damaged; - synchronized (parent) { - if (isUnlinked(parent.shrinkOVL) || node.parent != parent) { - return SpecialRetry; - } - - synchronized (node) { - prev = node.vOpt; - if (!shouldUpdate(func, prev, expected)) { - return noUpdateResult(func, prev); - } - if (prev == null) { - return updateResult(func, prev); - } - if (!attemptUnlink_nl(parent, node)) { - return SpecialRetry; - } - } - // try to fix the parent while we've still got the lock - damaged = fixHeight_nl(parent); - } - fixHeightAndRebalance(damaged); - return updateResult(func, prev); - } else { - // potential update (including remove-without-unlink) - synchronized (node) { - // regular version changes don't bother us - if (isUnlinked(node.shrinkOVL)) { - return SpecialRetry; - } - - final Object prev = node.vOpt; - if (!shouldUpdate(func, prev, expected)) { - return noUpdateResult(func, prev); - } - - // retry if we now detect that unlink is possible - if (newValue == null && (node.left == null || node.right == null)) { - return SpecialRetry; - } - - // update in-place - node.vOpt = newValue; - - afterNodeUpdate_nl(node, newValue); - - return updateResult(func, prev); - } - } - } - - protected void afterNodeUpdate_nl(Node<K,V> node, Object val) { - } - - /** Does not adjust the size or any heights. */ - private boolean attemptUnlink_nl(final Node<K,V> parent, final Node<K,V> node) { - // assert (Thread.holdsLock(parent)); - // assert (Thread.holdsLock(node)); - assert (!isUnlinked(parent.shrinkOVL)); - - final Node<K,V> parentL = parent.left; - final Node<K,V> parentR = parent.right; - if (parentL != node && parentR != node) { - // node is no longer a child of parent - return false; - } - - assert (!isUnlinked(node.shrinkOVL)); - assert (parent == node.parent); - - final Node<K,V> left = node.unsharedLeft(); - final Node<K,V> right = node.unsharedRight(); - if (left != null && right != null) { - // splicing is no longer possible - return false; - } - final Node<K,V> splice = left != null ? left : right; - - if (parentL == node) { - parent.left = splice; - } else { - parent.right = splice; - } - if (splice != null) { - splice.parent = parent; - } - - node.shrinkOVL = UnlinkedOVL; - node.vOpt = null; - - return true; - } - - //////////////// NavigableMap stuff - - @Override - public Map.Entry<K,V> pollFirstEntry() { - return pollExtremeEntry(Left); - } - - @Override - public Map.Entry<K,V> pollLastEntry() { - return pollExtremeEntry(Right); - } - - private Map.Entry<K,V> pollExtremeEntry(final char dir) { - final Epoch.Ticket ticket = holderRef.beginMutation(); - int sizeDelta = 0; - try { - final Map.Entry<K,V> prev = pollExtremeEntryUnderRoot(dir, holderRef.mutable()); - if (prev != null) { - sizeDelta = -1; - } - return prev; - } finally { - ticket.leave(sizeDelta); - } - } - - private Map.Entry<K,V> pollExtremeEntryUnderRoot(final char dir, final RootHolder<K,V> holder) { - while (true) { - final Node<K,V> right = holder.unsharedRight(); - if (right == null) { - // tree is empty, nothing to remove - return null; - } else { - final long ovl = right.shrinkOVL; - if (isShrinkingOrUnlinked(ovl)) { - right.waitUntilShrinkCompleted(ovl); - // RETRY - } else if (right == holder.right) { - // this is the protected .right - final Map.Entry<K,V> result = attemptRemoveExtreme(dir, holder, right, ovl); - if (result != SpecialRetry) { - return result; - } - // else RETRY - } - } - } - } - - private Map.Entry<K,V> attemptRemoveExtreme(final char dir, - final Node<K,V> parent, - final Node<K,V> node, - final long nodeOVL) { - assert (nodeOVL != UnlinkedOVL); - - while (true) { - final Node<K,V> child = node.unsharedChild(dir); - - if (nodeOVL != node.shrinkOVL) { - return null; - } - - if (child == null) { - // potential unlink, get ready by locking the parent - final Object vo; - final Node<K,V> damaged; - synchronized (parent) { - if (isUnlinked(parent.shrinkOVL) || node.parent != parent) { - return null; - } - - synchronized (node) { - vo = node.vOpt; - if (node.child(dir) != null || !attemptUnlink_nl(parent, node)) { - return null; - } - // success! - } - // try to fix parent.height while we've still got the lock - damaged = fixHeight_nl(parent); - } - fixHeightAndRebalance(damaged); - return new SimpleImmutableEntry<K,V>(node.key, decodeNull(vo)); - } else { - // keep going down - final long childOVL = child.shrinkOVL; - if (isShrinkingOrUnlinked(childOVL)) { - child.waitUntilShrinkCompleted(childOVL); - // RETRY - } else if (child != node.child(dir)) { - // this second read is important, because it is protected - // by childOVL - // RETRY - } else { - // validate the read that our caller took to get to node - if (node.shrinkOVL != nodeOVL) { - return null; - } - - final Map.Entry<K,V> result = attemptRemoveExtreme(dir, node, child, childOVL); - if (result != null) { - return result; - } - // else RETRY - } - } - } - } - - - - //////////////// tree balance and height info repair - - private static final int UnlinkRequired = -1; - private static final int RebalanceRequired = -2; - private static final int NothingRequired = -3; - - private int nodeCondition(final Node<K,V> node) { - // Begin atomic. - - final Node<K,V> nL = node.left; - final Node<K,V> nR = node.right; - - if ((nL == null || nR == null) && node.vOpt == null) { - return UnlinkRequired; - } - - final int hN = node.height; - final int hL0 = height(nL); - final int hR0 = height(nR); - - // End atomic. Since any thread that changes a node promises to fix - // it, either our read was consistent (and a NothingRequired conclusion - // is correct) or someone else has taken responsibility for either node - // or one of its children. - - final int hNRepl = 1 + Math.max(hL0, hR0); - final int bal = hL0 - hR0; - - if (bal < -1 || bal > 1) { - return RebalanceRequired; - } - - return hN != hNRepl ? hNRepl : NothingRequired; - } - - private void fixHeightAndRebalance(Node<K,V> node) { - while (node != null && node.parent != null) { - final int condition = nodeCondition(node); - if (condition == NothingRequired || isUnlinked(node.shrinkOVL)) { - // nothing to do, or no point in fixing this node - return; - } - - if (condition != UnlinkRequired && condition != RebalanceRequired) { - synchronized (node) { - node = fixHeight_nl(node); - } - } else { - final Node<K,V> nParent = node.parent; - synchronized (nParent) { - if (!isUnlinked(nParent.shrinkOVL) && node.parent == nParent) { - synchronized (node) { - node = rebalance_nl(nParent, node); - } - } - // else RETRY - } - } - } - } - - /** Attempts to fix the height of a (locked) damaged node, returning the - * lowest damaged node for which this thread is responsible. Returns null - * if no more repairs are needed. - */ - private Node<K,V> fixHeight_nl(final Node<K,V> node) { - final int c = nodeCondition(node); - switch (c) { - case RebalanceRequired: - case UnlinkRequired: - // can't repair - return node; - case NothingRequired: - // Any future damage to this node is not our responsibility. - return null; - default: - node.height = c; - // we've damaged our parent, but we can't fix it now - return node.parent; - } - } - - /** nParent and n must be locked on entry. Returns a damaged node, or null - * if no more rebalancing is necessary. - */ - private Node<K,V> rebalance_nl(final Node<K,V> nParent, final Node<K,V> n) { - - final Node<K,V> nL = n.unsharedLeft(); - final Node<K,V> nR = n.unsharedRight(); - - if ((nL == null || nR == null) && n.vOpt == null) { - if (attemptUnlink_nl(nParent, n)) { - // attempt to fix nParent.height while we've still got the lock - return fixHeight_nl(nParent); - } else { - // retry needed for n - return n; - } - } - - final int hN = n.height; - final int hL0 = height(nL); - final int hR0 = height(nR); - final int hNRepl = 1 + Math.max(hL0, hR0); - final int bal = hL0 - hR0; - - if (bal > 1) { - return rebalanceToRight_nl(nParent, n, nL, hR0); - } else if (bal < -1) { - return rebalanceToLeft_nl(nParent, n, nR, hL0); - } else if (hNRepl != hN) { - // we've got more than enough locks to do a height change, no need to - // trigger a retry - n.height = hNRepl; - - // nParent is already locked, let's try to fix it too - return fixHeight_nl(nParent); - } else { - // nothing to do - return null; - } - } - - private Node<K,V> rebalanceToRight_nl(final Node<K,V> nParent, - final Node<K,V> n, - final Node<K,V> nL, - final int hR0) { - // L is too large, we will rotate-right. If L.R is taller - // than L.L, then we will first rotate-left L. - synchronized (nL) { - final int hL = nL.height; - if (hL - hR0 <= 1) { - return n; // retry - } else { - final Node<K,V> nLR = nL.unsharedRight(); - final int hLL0 = height(nL.left); - final int hLR0 = height(nLR); - if (hLL0 >= hLR0) { - // rotate right based on our snapshot of hLR - return rotateRight_nl(nParent, n, nL, hR0, hLL0, nLR, hLR0); - } else { - synchronized (nLR) { - // If our hLR snapshot is incorrect then we might - // actually need to do a single rotate-right on n. - final int hLR = nLR.height; - if (hLL0 >= hLR) { - return rotateRight_nl(nParent, n, nL, hR0, hLL0, nLR, hLR); - } else { - // If the underlying left balance would not be - // sufficient to actually fix n.left, then instead - // of rolling it into a double rotation we do it on - // it's own. This may let us avoid rotating n at - // all, but more importantly it avoids the creation - // of damaged nodes that don't have a direct - // ancestry relationship. The recursive call to - // rebalanceToRight_nl in this case occurs after we - // release the lock on nLR. - // - // We also need to avoid damaging n.left if post- - // rotation it would be an unnecessary routing node. - // Note that although our height snapshots might be - // stale, their zero/non-zero state can't be. - final int hLRL = height(nLR.left); - final int b = hLL0 - hLRL; - if (b >= -1 && b <= 1 && !((hLL0 == 0 || hLRL == 0) && nL.vOpt == null)) { - // nParent.child.left won't be damaged after a double rotation - return rotateRightOverLeft_nl(nParent, n, nL, hR0, hLL0, nLR, hLRL); - } - } - } - // focus on nL, if necessary n will be balanced later - return rebalanceToLeft_nl(n, nL, nLR, hLL0); - } - } - } - } - - private Node<K,V> rebalanceToLeft_nl(final Node<K,V> nParent, - final Node<K,V> n, - final Node<K,V> nR, - final int hL0) { - synchronized (nR) { - final int hR = nR.height; - if (hL0 - hR >= -1) { - return n; // retry - } else { - final Node<K,V> nRL = nR.unsharedLeft(); - final int hRL0 = height(nRL); - final int hRR0 = height(nR.right); - if (hRR0 >= hRL0) { - return rotateLeft_nl(nParent, n, hL0, nR, nRL, hRL0, hRR0); - } else { - synchronized (nRL) { - final int hRL = nRL.height; - if (hRR0 >= hRL) { - return rotateLeft_nl(nParent, n, hL0, nR, nRL, hRL, hRR0); - } else { - final int hRLR = height(nRL.right); - final int b = hRR0 - hRLR; - if (b >= -1 && b <= 1 && !((hRR0 == 0 || hRLR == 0) && nR.vOpt == null)) { - return rotateLeftOverRight_nl(nParent, n, hL0, nR, nRL, hRR0, hRLR); - } - } - } - return rebalanceToRight_nl(n, nR, nRL, hRR0); - } - } - } - } - - private Node<K,V> rotateRight_nl(final Node<K,V> nParent, - final Node<K,V> n, - final Node<K,V> nL, - final int hR, - final int hLL, - final Node<K,V> nLR, - final int hLR) { - final long nodeOVL = n.shrinkOVL; - - final Node<K,V> nPL = nParent.left; - - n.shrinkOVL = beginChange(nodeOVL); - - n.left = nLR; - if (nLR != null) { - nLR.parent = n; - } - - nL.right = n; - n.parent = nL; - - if (nPL == n) { - nParent.left = nL; - } else { - nParent.right = nL; - } - nL.parent = nParent; - - // fix up heights links - final int hNRepl = 1 + Math.max(hLR, hR); - n.height = hNRepl; - nL.height = 1 + Math.max(hLL, hNRepl); - - n.shrinkOVL = endChange(nodeOVL); - - // We have damaged nParent, n (now parent.child.right), and nL (now - // parent.child). n is the deepest. Perform as many fixes as we can - // with the locks we've got. - - // We've already fixed the height for n, but it might still be outside - // our allowable balance range. In that case a simple fixHeight_nl - // won't help. - final int balN = hLR - hR; - if (balN < -1 || balN > 1) { - // we need another rotation at n - return n; - } - - // we've fixed balance and height damage for n, now handle - // extra-routing node damage - if ((nLR == null || hR == 0) && n.vOpt == null) { - // we need to remove n and then repair - return n; - } - - // we've already fixed the height at nL, do we need a rotation here? - final int balL = hLL - hNRepl; - if (balL < -1 || balL > 1) { - return nL; - } - - // nL might also have routing node damage (if nL.left was null) - if (hLL == 0 && nL.vOpt == null) { - return nL; - } - - // try to fix the parent height while we've still got the lock - return fixHeight_nl(nParent); - } - - private Node<K,V> rotateLeft_nl(final Node<K,V> nParent, - final Node<K,V> n, - final int hL, - final Node<K,V> nR, - final Node<K,V> nRL, - final int hRL, - final int hRR) { - final long nodeOVL = n.shrinkOVL; - - final Node<K,V> nPL = nParent.left; - - n.shrinkOVL = beginChange(nodeOVL); - - // fix up n links, careful to be compatible with concurrent traversal for all but n - n.right = nRL; - if (nRL != null) { - nRL.parent = n; - } - - nR.left = n; - n.parent = nR; - - if (nPL == n) { - nParent.left = nR; - } else { - nParent.right = nR; - } - nR.parent = nParent; - - // fix up heights - final int hNRepl = 1 + Math.max(hL, hRL); - n.height = hNRepl; - nR.height = 1 + Math.max(hNRepl, hRR); - - n.shrinkOVL = endChange(nodeOVL); - - final int balN = hRL - hL; - if (balN < -1 || balN > 1) { - return n; - } - - if ((nRL == null || hL == 0) && n.vOpt == null) { - return n; - } - - final int balR = hRR - hNRepl; - if (balR < -1 || balR > 1) { - return nR; - } - - if (hRR == 0 && nR.vOpt == null) { - return nR; - } - - return fixHeight_nl(nParent); - } - - private Node<K,V> rotateRightOverLeft_nl(final Node<K,V> nParent, - final Node<K,V> n, - final Node<K,V> nL, - final int hR, - final int hLL, - final Node<K,V> nLR, - final int hLRL) { - final long nodeOVL = n.shrinkOVL; - final long leftOVL = nL.shrinkOVL; - - final Node<K,V> nPL = nParent.left; - final Node<K,V> nLRL = nLR.unsharedLeft(); - final Node<K,V> nLRR = nLR.unsharedRight(); - final int hLRR = height(nLRR); - - n.shrinkOVL = beginChange(nodeOVL); - nL.shrinkOVL = beginChange(leftOVL); - - // fix up n links, careful about the order! - n.left = nLRR; - if (nLRR != null) { - nLRR.parent = n; - } - - nL.right = nLRL; - if (nLRL != null) { - nLRL.parent = nL; - } - - nLR.left = nL; - nL.parent = nLR; - nLR.right = n; - n.parent = nLR; - - if (nPL == n) { - nParent.left = nLR; - } else { - nParent.right = nLR; - } - nLR.parent = nParent; - - // fix up heights - final int hNRepl = 1 + Math.max(hLRR, hR); - n.height = hNRepl; - final int hLRepl = 1 + Math.max(hLL, hLRL); - nL.height = hLRepl; - nLR.height = 1 + Math.max(hLRepl, hNRepl); - - n.shrinkOVL = endChange(nodeOVL); - nL.shrinkOVL = endChange(leftOVL); - - // caller should have performed only a single rotation if nL was going - // to end up damaged - assert(Math.abs(hLL - hLRL) <= 1); - assert(!((hLL == 0 || nLRL == null) && nL.vOpt == null)); - - // We have damaged nParent, nLR (now parent.child), and n (now - // parent.child.right). n is the deepest. Perform as many fixes as we - // can with the locks we've got. - - // We've already fixed the height for n, but it might still be outside - // our allowable balance range. In that case a simple fixHeight_nl - // won't help. - final int balN = hLRR - hR; - if (balN < -1 || balN > 1) { - // we need another rotation at n - return n; - } - - // n might also be damaged by being an unnecessary routing node - if ((nLRR == null || hR == 0) && n.vOpt == null) { - // repair involves splicing out n and maybe more rotations - return n; - } - - // we've already fixed the height at nLR, do we need a rotation here? - final int balLR = hLRepl - hNRepl; - if (balLR < -1 || balLR > 1) { - return nLR; - } - - // try to fix the parent height while we've still got the lock - return fixHeight_nl(nParent); - } - - private Node<K,V> rotateLeftOverRight_nl(final Node<K,V> nParent, - final Node<K,V> n, - final int hL, - final Node<K,V> nR, - final Node<K,V> nRL, - final int hRR, - final int hRLR) { - final long nodeOVL = n.shrinkOVL; - final long rightOVL = nR.shrinkOVL; - - final Node<K,V> nPL = nParent.left; - final Node<K,V> nRLL = nRL.unsharedLeft(); - final Node<K,V> nRLR = nRL.unsharedRight(); - final int hRLL = height(nRLL); - - n.shrinkOVL = beginChange(nodeOVL); - nR.shrinkOVL = beginChange(rightOVL); - - // fix up n links, careful about the order! - n.right = nRLL; - if (nRLL != null) { - nRLL.parent = n; - } - - nR.left = nRLR; - if (nRLR != null) { - nRLR.parent = nR; - } - - nRL.right = nR; - nR.parent = nRL; - nRL.left = n; - n.parent = nRL; - - if (nPL == n) { - nParent.left = nRL; - } else { - nParent.right = nRL; - } - nRL.parent = nParent; - - // fix up heights - final int hNRepl = 1 + Math.max(hL, hRLL); - n.height = hNRepl; - final int hRRepl = 1 + Math.max(hRLR, hRR); - nR.height = hRRepl; - nRL.height = 1 + Math.max(hNRepl, hRRepl); - - n.shrinkOVL = endChange(nodeOVL); - nR.shrinkOVL = endChange(rightOVL); - - assert(Math.abs(hRR - hRLR) <= 1); - - final int balN = hRLL - hL; - if (balN < -1 || balN > 1) { - return n; - } - if ((nRLL == null || hL == 0) && n.vOpt == null) { - return n; - } - final int balRL = hRRepl - hNRepl; - if (balRL < -1 || balRL > 1) { - return nRL; - } - return fixHeight_nl(nParent); - } - - //////////////// Map views - - @Override - public NavigableSet<K> keySet() { - return navigableKeySet(); - } - - @Override - public Set<Map.Entry<K,V>> entrySet() { - return new EntrySet(); - } - - private class EntrySet extends AbstractSet<Map.Entry<K,V>> { - - @Override - public int size() { - return SnapTreeMap.this.size(); - } - - @Override - public boolean isEmpty() { - return SnapTreeMap.this.isEmpty(); - } - - @Override - public void clear() { - SnapTreeMap.this.clear(); - } - - @Override - public boolean contains(final Object o) { - if (!(o instanceof Map.Entry<?,?>)) { - return false; - } - final Object k = ((Map.Entry<?,?>)o).getKey(); - final Object v = ((Map.Entry<?,?>)o).getValue(); - final Object actualVo = SnapTreeMap.this.getImpl(k); - if (actualVo == null) { - // no associated value - return false; - } - final V actual = decodeNull(actualVo); - return v == null ? actual == null : v.equals(actual); - } - - @Override - public boolean add(final Entry<K,V> e) { - final Object v = encodeNull(e.getValue()); - return update(e.getKey(), UpdateAlways, null, v) != v; - } - - @Override - public boolean remove(final Object o) { - if (!(o instanceof Map.Entry<?,?>)) { - return false; - } - final Object k = ((Map.Entry<?,?>)o).getKey(); - final Object v = ((Map.Entry<?,?>)o).getValue(); - return SnapTreeMap.this.remove(k, v); - } - - @Override - public Iterator<Entry<K,V>> iterator() { - return new EntryIter<K,V>(SnapTreeMap.this); - } - } - - private static class EntryIter<K,V> extends AbstractIter<K,V> implements Iterator<Map.Entry<K,V>> { - private EntryIter(final SnapTreeMap<K,V> m) { - super(m); - } - - private EntryIter(final SnapTreeMap<K,V> m, - final Comparable<? super K> minCmp, - final boolean minIncl, - final Comparable<? super K> maxCmp, - final boolean maxIncl, - final boolean descending) { - super(m, minCmp, minIncl, maxCmp, maxIncl, descending); - } - - @Override - public Entry<K,V> next() { - return nextNode(); - } - } - - private static class KeyIter<K,V> extends AbstractIter<K,V> implements Iterator<K> { - private KeyIter(final SnapTreeMap<K,V> m) { - super(m); - } - - private KeyIter(final SnapTreeMap<K,V> m, - final Comparable<? super K> minCmp, - final boolean minIncl, - final Comparable<? super K> maxCmp, - final boolean maxIncl, - final boolean descending) { - super(m, minCmp, minIncl, maxCmp, maxIncl, descending); - } - - @Override - public K next() { - return nextNode().key; - } - } - - private static class AbstractIter<K,V> { - private final SnapTreeMap<K,V> m; - private final boolean descending; - private final char forward; - private final char reverse; - private Node<K,V>[] path; - private int depth = 0; - private Node<K,V> mostRecentNode; - private final K endKey; - - @SuppressWarnings("unchecked") - AbstractIter(final SnapTreeMap<K,V> m) { - this.m = m; - this.descending = false; - this.forward = Right; - this.reverse = Left; - final Node<K,V> root = m.holderRef.frozen().right; - this.path = (Node<K,V>[]) new Node[1 + height(root)]; - this.endKey = null; - pushFirst(root); - } - - @SuppressWarnings("unchecked") - AbstractIter(final SnapTreeMap<K,V> m, - final Comparable<? super K> minCmp, - final boolean minIncl, - final Comparable<? super K> maxCmp, - final boolean maxIncl, - final boolean descending) { - this.m = m; - this.descending = descending; - this.forward = !descending ? Right : Left; - this.reverse = !descending ? Left : Right; - final Comparable<? super K> fromCmp; - final boolean fromIncl = !descending ? minIncl : maxIncl; - final Comparable<? super K> toCmp; - final boolean toIncl = !descending ? maxIncl : minIncl; - if (!descending) { - fromCmp = minCmp; - toCmp = maxCmp; - } else { - fromCmp = maxCmp; - toCmp = minCmp; - } - - final Node<K,V> root = m.holderRef.frozen().right; - - if (toCmp != null) { - this.endKey = (K) m.boundedExtreme(minCmp, minIncl, maxCmp, maxIncl, true, forward); - if (this.endKey == null) { - // no node satisfies the bound, nothing to iterate - // ---------> EARLY EXIT - return; - } - } else { - this.endKey = null; - } - - this.path = (Node<K,V>[]) new Node[1 + height(root)]; - - if (fromCmp == null) { - pushFirst(root); - } - else { - pushFirst(root, fromCmp, fromIncl); - if (depth > 0 && top().vOpt == null) { - advance(); - } - } - } - - private int cmp(final Comparable<? super K> comparable, final K key) { - final int c = comparable.compareTo(key); - if (!descending) { - return c; - } else { - return c == Integer.MIN_VALUE ? 1 : -c; - } - } - - private void pushFirst(Node<K,V> node) { - while (node != null) { - path(node); - node = node.child(reverse); - } - } - - private void path(Node<K,V> node) { - if (depth == path.length) - path = Arrays.copyOf(path, depth + 2); - - path[depth++] = node; - } - - private void pushFirst(Node<K,V> node, final Comparable<? super K> fromCmp, final boolean fromIncl) { - while (node != null) { - final int c = cmp(fromCmp, node.key); - if (c > 0 || (c == 0 && !fromIncl)) { - // everything we're interested in is on the right - node = node.child(forward); - } - else { - path(node); - if (c == 0) { - // start the iteration here - return; - } - else { - node = node.child(reverse); - } - } - } - } - - private Node<K,V> top() { - return path[depth - 1]; - } - - private void advance() { - do { - final Node<K,V> t = top(); - if (endKey != null && endKey == t.key) { - depth = 0; - path = null; - return; - } - - final Node<K,V> fwd = t.child(forward); - if (fwd != null) { - pushFirst(fwd); - } else { - // keep going up until we pop a node that is a left child - Node<K,V> popped; - do { - popped = path[--depth]; - } while (depth > 0 && popped == top().child(forward)); - } - - if (depth == 0) { - // clear out the path so we don't pin too much stuff - path = null; - return; - } - - // skip removed-but-not-unlinked entries - } while (top().vOpt == null); - } - - public boolean hasNext() { - return depth > 0; - } - - Node<K,V> nextNode() { - if (depth == 0) { - throw new NoSuchElementException(); - } - mostRecentNode = top(); - advance(); - return mostRecentNode; - } - - public void remove() { - if (mostRecentNode == null) { - throw new IllegalStateException(); - } - m.remove(mostRecentNode.key); - mostRecentNode = null; - } - } - - //////////////// navigable keySet - - @Override - public NavigableSet<K> navigableKeySet() { - return new KeySet<K>(this) { - public Iterator<K> iterator() { - return new KeyIter<K,V>(SnapTreeMap.this); - } - }; - } - - @Override - public NavigableSet<K> descendingKeySet() { - return descendingMap().navigableKeySet(); - } - - private abstract static class KeySet<K> extends AbstractSet<K> implements NavigableSet<K> { - - private final ConcurrentNavigableMap<K,?> map; - - protected KeySet(final ConcurrentNavigableMap<K,?> map) { - this.map = map; - } - - //////// basic Set stuff - - @Override - abstract public Iterator<K> iterator(); - - @Override - public boolean contains(final Object o) { return map.containsKey(o); } - @Override - public boolean isEmpty() { return map.isEmpty(); } - @Override - public int size() { return map.size(); } - @Override - public boolean remove(final Object o) { return map.remove(o) != null; } - - //////// SortedSet stuff - - @Override - public Comparator<? super K> comparator() { return map.comparator(); } - @Override - public K first() { return map.firstKey(); } - @Override - public K last() { return map.lastKey(); } - - //////// NavigableSet stuff - - @Override - public K lower(final K k) { return map.lowerKey(k); } - @Override - public K floor(final K k) { return map.floorKey(k); } - @Override - public K ceiling(final K k) { return map.ceilingKey(k); } - @Override - public K higher(final K k) { return map.higherKey(k); } - - @Override - public K pollFirst() { return map.pollFirstEntry().getKey(); } - @Override - public K pollLast() { return map.pollLastEntry().getKey(); } - - @Override - public NavigableSet<K> descendingSet() { return map.descendingKeySet(); } - @Override - public Iterator<K> descendingIterator() { return map.descendingKeySet().iterator(); } - - @Override - public NavigableSet<K> subSet(final K fromElement, final boolean minInclusive, final K toElement, final boolean maxInclusive) { - return map.subMap(fromElement, minInclusive, toElement, maxInclusive).keySet(); - } - @Override - public NavigableSet<K> headSet(final K toElement, final boolean inclusive) { - return map.headMap(toElement, inclusive).keySet(); - } - @Override - public NavigableSet<K> tailSet(final K fromElement, final boolean inclusive) { - return map.tailMap(fromElement, inclusive).keySet(); - } - @Override - public SortedSet<K> subSet(final K fromElement, final K toElement) { - return map.subMap(fromElement, toElement).keySet(); - } - @Override - public SortedSet<K> headSet(final K toElement) { - return map.headMap(toElement).keySet(); - } - @Override - public SortedSet<K> tailSet(final K fromElement) { - return map.tailMap(fromElement).keySet(); - } - } - - //////////////// NavigableMap views - - @Override - public ConcurrentNavigableMap<K,V> subMap(final K fromKey, - final boolean fromInclusive, - final K toKey, - final boolean toInclusive) { - final Comparable<? super K> fromCmp = comparable(fromKey); - if (fromCmp.compareTo(toKey) > 0) { - throw new IllegalArgumentException(); - } - return new SubMap<K,V>(this, fromKey, fromCmp, fromInclusive, toKey, comparable(toKey), toInclusive, false); - } - - @Override - public ConcurrentNavigableMap<K,V> headMap(final K toKey, final boolean inclusive) { - return new SubMap<K,V>(this, null, null, false, toKey, comparable(toKey), inclusive, false); - } - - @Override - public ConcurrentNavigableMap<K,V> tailMap(final K fromKey, final boolean inclusive) { - return new SubMap<K,V>(this, fromKey, comparable(fromKey), inclusive, null, null, false, false); - } - - @Override - public ConcurrentNavigableMap<K,V> subMap(final K fromKey, final K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public ConcurrentNavigableMap<K,V> headMap(final K toKey) { - return headMap(toKey, false); - } - - @Override - public ConcurrentNavigableMap<K,V> tailMap(final K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public ConcurrentNavigableMap<K,V> descendingMap() { - return new SubMap(this, null, null, false, null, null, false, true); - } - - private static class SubMap<K, V> extends AbstractMap<K, V> implements ConcurrentNavigableMap<K, V>, Serializable { - /** */ - private static final long serialVersionUID = 0L; - - private final SnapTreeMap<K,V> m; - private final K minKey; - private transient Comparable<? super K> minCmp; - private final boolean minIncl; - private final K maxKey; - private transient Comparable<? super K> maxCmp; - private final boolean maxIncl; - private final boolean descending; - - private SubMap(final SnapTreeMap<K,V> m, - final K minKey, - final Comparable<? super K> minCmp, - final boolean minIncl, - final K maxKey, - final Comparable<? super K> maxCmp, - final boolean maxIncl, - final boolean descending) { - this.m = m; - this.minKey = minKey; - this.minCmp = minCmp; - this.minIncl = minIncl; - this.maxKey = maxKey; - this.maxCmp = maxCmp; - this.maxIncl = maxIncl; - this.descending = descending; - } - - // TODO: clone - - private boolean tooLow(final K key) { - if (minCmp == null) { - return false; - } else { - final int c = minCmp.compareTo(key); - return c > 0 || (c == 0 && !minIncl); - } - } - - private boolean tooHigh(final K key) { - if (maxCmp == null) { - return false; - } else { - final int c = maxCmp.compareTo(key); - return c < 0 || (c == 0 && !maxIncl); - } - } - - private boolean inRange(final K key) { - return !tooLow(key) && !tooHigh(key); - } - - private void requireInRange(final K key) { - if (key == null) { - throw new NullPointerException(); - } - if (!inRange(key)) { - throw new IllegalArgumentException(); - } - } - - private char minDir() { - return descending ? Right : Left; - } - - private char maxDir() { - return descending ? Left : Right; - } - - //////// AbstractMap - - @Override - public boolean isEmpty() { - return m.boundedExtreme(minCmp, minIncl, maxCmp, maxIncl, true, Left) == null; - } - - @Override - public int size() { - final Node<K,V> root = m.holderRef.frozen().right; - return Node.computeFrozenSize(root, minCmp, minIncl, maxCmp, maxIncl); - } - - @Override - @SuppressWarnings("unchecked") - public boolean containsKey(final Object key) { - if (key == null) { - throw new NullPointerException(); - } - final K k = (K) key; - return inRange(k) && m.containsKey(k); - } - - @Override - public boolean containsValue(final Object value) { - /
<TRUNCATED>