package skiplists.lockfree; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.NavigableSet; import java.util.NoSuchElementException; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.locks.ReentrantLock; import contention.abstractions.CompositionalMap; import contention.abstractions.MaintenanceAlg; /** * The No Hot Spot Non-Blocking Skip List * as presented in the paper from Crain, Gramoli and Raynal * that appeared at ICDCS 2013. * * @author Tyler Crain * * @param <K> * The key * @param <V> * The value */ public class NonBlockingFriendlySkipListMap<K, V> extends AbstractMap<K, V> implements CompositionalMap<K, V>, MaintenanceAlg, ConcurrentNavigableMap<K, V> { /** * If set to true then a maintenance thread is used */ private static final boolean maintenance = true; /** * If set to true, then the maintenance thread will physically remove nodes */ private static final boolean removeInMainteance = true; /** * If set to true then the delete operation will physically remove nodes */ private static final boolean removeInDelete = true; /** * If set to true, then the algorithms will raise the bottom index level if * necessary */ private static final boolean doBottomLevelRaises = true; private static final boolean useFairLocks = false; private static final boolean tryLock = true; /** * List of all skip lists in the system that should be maintained by the * maintenance thread (there is only one maintenance thread in the system) */ private static final ConcurrentLinkedQueue<NonBlockingFriendlySkipListMap> skipLists = new ConcurrentLinkedQueue<NonBlockingFriendlySkipListMap>(); /** * Number of times the bottom indexItem level has been removed */ private final AtomicInteger bottomLevelRaiseCount = new AtomicInteger(); /** * Class used to keep track of the counts of the maintenance operations done * */ private class MaintVars { private volatile int maxHeight = 1; private long heightChanges = 0; private long tallDeletedCount = 0, totalCount = 0, nonDeleted = 0; private long removals = 0; } /** * Used to decide when to perform the maintenance */ private final MaintVars vars = new MaintVars(); /** * The maximum height the skip list can reach */ private static final int totalHeight = 31; /** * The height of the skip list to start */ private static final int initialHeight = 6; static final class ComparableUsingComparator<K> implements Comparable<K> { final K actualKey; final Comparator<? super K> cmp; ComparableUsingComparator(K key, Comparator<? super K> cmp) { this.actualKey = key; this.cmp = cmp; } public int compareTo(K k2) { return cmp.compare(actualKey, k2); } } private final Comparator<? super K> comparator; private Comparable<? super K> comparable(Object key) throws ClassCastException { if (key == null) throw new NullPointerException(); if (comparator != null) return new ComparableUsingComparator<K>((K) key, comparator); else return (Comparable<? super K>) key; } /** * This lock is used when maintenance is distributed among application * threads, the thread doing the maintenance must own this lock * * No longer used! */ private final ReentrantLock maintLock = new ReentrantLock(useFairLocks); /** * Extension of thread class, the maintenance thread is an instance of this * class * */ private static class MaintenanceThread extends Thread { public void run() { doMaintenance(); } } /** * Instances of this class make up the upper levels of the skip-list * * @param <K> * The key type * @param <V> * The value type */ static class Index<K, V> { /** * The node at the bottom list level that this Index corresponds to */ final Node<K, V> node; /** * The Index item one level below this one, or null if this is already * the bottom index level */ final Index<K, V> down; /** * The index item to the right, or null if this is the end of this level */ volatile Index<K, V> right; /** * Creates index node with given values. */ Index(Node<K, V> node, Index<K, V> down, Index<K, V> right) { this.node = node; this.down = down; this.right = right; } /** Updater for casRight */ static final AtomicReferenceFieldUpdater<Index, Index> rightUpdater = AtomicReferenceFieldUpdater .newUpdater(Index.class, Index.class, "right"); /** * compareAndSet right field */ final boolean casRight(Index<K, V> cmp, Index<K, V> val) { // If the Index levels of the skip list are maintained by the // maintenance, then // we don't need synchronization if (maintenance) { right = val; return true; } return rightUpdater.compareAndSet(this, cmp, val); } } /** * Nodes make up the bottom level of the skip list * * @param <K> * The key type * @param <V> * The value type */ static final class Node<K, V> { final K key; volatile V value; volatile Node<K, V> next, prev; final ReentrantLock lock = new ReentrantLock(useFairLocks); final class LevelVars { volatile int topLevel = 0; boolean updated = false; // does this need to be volatile? volatile Index<K, V> up = null; } final LevelVars vars = new LevelVars(); /** * Creates a new regular node. */ Node(K key, V value) { this.key = key; this.value = value; } /** * Constructor for marker node, used during physical removal It has a * null key, and a value that points to itself */ private Node() { this.key = null; this.value = (V) this; } /** * Creates a marker node * * @param prev * the previous node in the list * @param next * the next node in the list * @return the new marker node */ static final Node newMarker(Node prev, Node next) { Node node = new Node(); node.prev = prev; node.next = next; return node; } /** Updater for casNext */ static final AtomicReferenceFieldUpdater<Node, Node> nextUpdater = AtomicReferenceFieldUpdater .newUpdater(Node.class, Node.class, "next"); /** * compareAndSet next field */ final boolean casNext(Node<K, V> cmp, Node<K, V> val) { return nextUpdater.compareAndSet(this, cmp, val); } /** Updater for casValue */ static final AtomicReferenceFieldUpdater<Node, Object> valueUpdater = AtomicReferenceFieldUpdater .newUpdater(Node.class, Object.class, "value"); /** * compareAndSet value field */ final boolean casValue(Object cmp, Object val) { return valueUpdater.compareAndSet(this, cmp, val); } } /** * Instances of this class are used to point to the beginning of the top and * bottom Index item lists * * @param <K> * The key type * @param <V> * The value type */ static class HeadPointer<K, V> { final Index<K, V> node; final int value; HeadPointer(Index<K, V> node, int value) { this.node = node; this.value = value; } } /** * Always the first node in the Node list */ private final Node<K, V> begin = new Node<K, V>((K) new Object(), null); /** * Pointer for the start of the top Index list */ private volatile HeadPointer<K, V> topStart; /** * Pointer for the start of the bottom Index list */ private volatile HeadPointer<K, V> bottomStart; /** Updater for topStart */ static final AtomicReferenceFieldUpdater<NonBlockingFriendlySkipListMap, HeadPointer> topStartUpdater = AtomicReferenceFieldUpdater .newUpdater(NonBlockingFriendlySkipListMap.class, HeadPointer.class, "topStart"); /** * compareAndSet topStart field */ final boolean casTopStart(HeadPointer<K, V> cmp, HeadPointer<K, V> val) { return topStartUpdater.compareAndSet(this, cmp, val); } /** Updater for bottomStart */ static final AtomicReferenceFieldUpdater<NonBlockingFriendlySkipListMap, HeadPointer> bottomStartUpdater = AtomicReferenceFieldUpdater .newUpdater(NonBlockingFriendlySkipListMap.class, HeadPointer.class, "bottomStart"); /** * compareAndSet topStart field */ final boolean casBottomStart(HeadPointer<K, V> cmp, HeadPointer<K, V> val) { return bottomStartUpdater.compareAndSet(this, cmp, val); } // For maintenance thread /** * Used to stop the maintenance loop */ static volatile boolean stop = false; /** * Instance of the maintenance thread This is static and atomic because we * have exactly one maintenance thread, even if we have multiple skip lists */ private static AtomicReference<MaintenanceThread> mainThd = new AtomicReference<MaintenanceThread>( null); // For when maintenance is run by the application threads /** * How often the thread should try to perform maintenance */ private final double maintPercentage; /** * If the maintenance should be done by application threads */ private final boolean seperateMaint; /** * Thread local random used to check if it should try to do maintenance when * maintenance is done by application threads */ final private static ThreadLocal<Random> s_random = new ThreadLocal<Random>() { @Override protected synchronized Random initialValue() { return new Random(); } }; /** * Array of Index pointers This array is used when a node's height is * raised, it keeps track of the previous item in the list at each level for * where the new Index item will be inserted for this node */ final private ThreadLocal<Index<K, V>[]> thdLocalPrevArray = new ThreadLocal<Index<K, V>[]>() { @Override protected synchronized Index<K, V>[] initialValue() { return (Index<K, V>[]) new Index[totalHeight + 1]; } }; // Constructors public NonBlockingFriendlySkipListMap() { this(null, 0); } public NonBlockingFriendlySkipListMap(double maintPercentage) { this(null, maintPercentage); } /** * Creates a new instance of the skip list * * @param comparator * for comparing * @param maintPercentage * if set to 0 then we have seperate maintenance */ public NonBlockingFriendlySkipListMap(Comparator<? super K> comparator, double maintPercentage) { this.comparator = comparator; this.maintPercentage = maintPercentage; initialize(); if (maintenance) { if (maintPercentage == 0) { this.seperateMaint = true; } else { this.seperateMaint = false; } this.startMaintenance(); } else { seperateMaint = false; } } /** * Initializes an empty skip list */ private void initialize() { values = null; begin.next = null; begin.vars.topLevel = totalHeight; // begin.vars.bottomLevel = 0; // The following lines create a new node of maximum initial height // used as the "root" node in the list, and points the beginning // pointers to this node and its index ArrayList<Index<K, V>> beginList = new ArrayList<Index<K, V>>( initialHeight); Index<K, V> nextBegin = null, prevBegin = new Index<K, V>(begin, null, null); bottomStart = new HeadPointer<K, V>(prevBegin, 0); beginList.add(0, nextBegin); for (int i = 1; i < initialHeight; i++) { nextBegin = new Index<K, V>(begin, prevBegin, null); beginList.add(i, nextBegin); prevBegin = nextBegin; } topStart = new HeadPointer<K, V>(nextBegin, initialHeight - 1); begin.vars.up = bottomStart.node; // Add this skip list to the list of skip lists to be maintained by // the maintenance thread if (maintenance) skipLists.add(this); } void finishCount2(int nodesTraversed) { Vars vars = counts.get(); vars.nodesTraversed += nodesTraversed; } void finishCount1(int nodesTraversed) { Vars vars = counts.get(); vars.getCount++; vars.nodesTraversed += nodesTraversed; } @Override public V get(final Object kkey) { HeadPointer<K, V> top = topStart, bottom = bottomStart; int nodesTraversed = 0; Comparable<? super K> key = comparable(kkey); // Get the suspected node previous to the one being searched for Node<K, V> prev = getPrevFast(key, top.node, top.value, bottom.value); for (;;) { Node<K, V> next = prev.next; if (TRAVERSAL_COUNT) { nodesTraversed++; } // Didn't find the node if (next == null) { if (TRAVERSAL_COUNT) { finishCount2(nodesTraversed); } return null; } K nextKey = next.key; int c; if (nextKey == null) // null key means marker, keep traversing c = 1; else c = key.compareTo(nextKey); if (c == 0) { // found the node if (TRAVERSAL_COUNT) { finishCount2(nodesTraversed); } V val = next.value; // check if it has been marked deleted if (val != next) { return val; } return null; } else if (c < 0) { if (TRAVERSAL_COUNT) { finishCount2(nodesTraversed); } return null; } // Not at the correct node so continue traversal prev = getPrevNode(key, next, false); } } @Override public boolean containsKey(Object key) { if (this.get(key) == null) { return false; } return true; } @Override public V putIfAbsent(K kkey, V value) { if (!lockFree) { return insertLocking(kkey, value, false); } return insertLockFree(kkey, value, false); } @Override public V put(K kkey, V value) { // TODO This is currently just putifabsent! if (!lockFree) { return insertLocking(kkey, value, true); } return insertLockFree(kkey, value, true); } /** * Lock free node insertion * * @param kkey * key to insert * @param value * value to insert * @param put * if false performs a put-if-absent, otherwise just a put * @return null if no node with key existed, otherwise the value of the node * with the key */ private V insertLockFree(K kkey, V value, boolean put) { HeadPointer<K, V> top = topStart, bottom = bottomStart; Comparable<? super K> key = comparable(kkey); Node<K, V> prev = null; Node<K, V> newNode = null; if (!maintenance) { // if there is no maintenance then we have to raise the node within // the insert prev = getPrev(key, topStart.node, top.value, bottom.value); } else { prev = getPrevFast(key, topStart.node, top.value, bottom.value); } int c; for (;;) { Node<K, V> next = prev.next; if (next == null) // end of the list c = -1; else { K nextKey = next.key; if (nextKey == null) { // marker, can't stop traversal here c = 1; } else c = key.compareTo(nextKey); } if (c == 0) { // found the node V val = next.value; // loop trying to finish the operation, if the value points to // the node, // then it has been physically removed, so exit loop and // continue traversal while (val != next) { if (val != null) { // node is not marked deleted if (put) { // if a put, then update the value if (next.casValue(val, value)) { return val; } } else { // not a put so just return the value return val; } } else { // node is marked deleted, undelete it! if (next.casValue(val, value)) return null; } val = next.value; } } else if (c < 0) { // didn't find the key, so insert a new node // but only do it if we are not at a marker node if (prev.value != prev && prev.key != null) { next = prev.next; if (newNode == null) { newNode = new Node<K, V>(kkey, value); } newNode.prev = prev; newNode.next = next; if (prev.casNext(next, newNode)) { if (next != null) { next.prev = newNode; } if (!maintenance) { // no maintenance, so must raise the node here raiseLevels(key, newNode, top, bottom.value); } return null; } } } // We were not at the right prev node, so continue traversal! // Will do helping in here prev = getPrevNode(key, prev, true); } } /** * Lock based node insertion * * @param kkey * key to insert * @param value * value to insert * @param put * if false performs a put-if-absent, otherwise just a put * @return null if no node with key existed, otherwise the value of the node * with the key */ public V insertLocking(K kkey, V value, boolean put) { HeadPointer<K, V> top = topStart, bottom = bottomStart; Comparable<? super K> key = comparable(kkey); Node<K, V> prev = null; if (!maintenance) { // if there is no maintenance then we have to raise the node within // the insert prev = getPrev(key, topStart.node, top.value, bottom.value); } else { prev = getPrevFast(key, topStart.node, top.value, bottom.value); } int c; for (;;) { Node<K, V> next = prev.next; if (next == null) // end of the list c = -1; else { K nextKey = next.key; if (nextKey == null) // continue traversal c = 1; else c = key.compareTo(nextKey); } if (c == 0) { // found a node with the key V val = next.value; if (val != next) { if (val != null && !put) // return immediately if the node exists and not marked // deleted return val; // lock the node next.lock.lock(); // ensure it hasn't been physically removed if (next.value != next) { val = next.value; // check if marked deleted if (val != null) { if (put) { next.value = value; } next.lock.unlock(); return val; } // mark it undeleted next.value = value; next.lock.unlock(); return null; } next.lock.unlock(); } } else if (c < 0) { // insert a new node // lock the previous node prev.lock.lock(); // ensure the node hasn't been physically removed if (prev.value != prev) { next = prev.next; // ensure that a node hasn't been concurrently inserted in // front of us if (next == null) c = -1; else { K nextKey = next.key; if (nextKey == null) c = 1; else c = key.compareTo(nextKey); } if (c < 0) { // we are still in the correct location, so insert a new // node Node<K, V> newNode = new Node<K, V>(kkey, value); newNode.prev = prev; newNode.next = next; if (next != null) { next.prev = newNode; } prev.next = newNode; prev.lock.unlock(); if (!maintenance) { // no maintenance, so must raise the node here raiseLevels(key, newNode, top, bottom.value); } return null; } } prev.lock.unlock(); } // We were not at the right prev node, so continue traversal! prev = getPrevNode(key, prev, true); } } @Override public V remove(final Object kkey) { if (!lockFree) { return removeLocking(kkey); } return removeLockFree(kkey); } /** * Lock based deletion * * @param kkey * the key to delete * @return null if no node with key was found, otherwise the value of the * node that was deleted */ private V removeLocking(final Object kkey) { HeadPointer<K, V> top = topStart, bottom = bottomStart; Comparable<? super K> key = comparable(kkey); // Find the previous node in the bottom list Node<K, V> prev = getPrevFast(key, top.node, top.value, bottom.value); for (;;) { Node<K, V> next = prev.next; // reached the end of the list if (next == null) return null; K nextKey = next.key; int c; if (nextKey == null) // continue traversal c = 1; else c = key.compareTo(nextKey); if (c == 0) { // found a node with the key V val = next.value; if (val == null || val == next) // it has been deleted return null; // lock the node next.lock.lock(); val = next.value; if (val == null || val == next) { // concurrent deletion next.lock.unlock(); return null; } // Mark deleted next.value = null; if (removeInDelete) { if (!removeInMainteance) { // no removals are done in maintenance, since removals // are best effort let us try // to remove this node as well as other marked deleted // nodes connected to this node removeMaintLoop(prev, next, bottomStart.value); } else { // removals are done also in maintenance, so just try to // remove this node removeMaint(prev, next, bottomStart.value); } } else { next.lock.unlock(); } return val; } else if (c < 0) // found no node with the key being searched for return null; // Not at the correct node so continue traversal prev = getPrevNode(key, next, false); } } /** * Lock-free deletion * * @param kkey * the key to delete * @return null if no node with key was found, otherwise the value of the * node that was deleted */ private V removeLockFree(final Object kkey) { HeadPointer<K, V> top = topStart, bottom = bottomStart; Comparable<? super K> key = comparable(kkey); // find the previous node in the list Node<K, V> prev = getPrevFast(key, top.node, top.value, bottom.value); for (;;) { Node<K, V> next = prev.next; if (next == null) // reached the end of the list without finding the key return null; K nextKey = next.key; int c; if (nextKey == null) // marker, continue traversal c = 1; else c = key.compareTo(nextKey); if (c == 0) { // found a node with the key // loop on this node, until the operation is finished, or is // physically removed concurrently while (true) { V val = next.value; if (val == null || val == next) return null; // Mark deleted if (next.casValue(val, null)) { if (removeInDelete) { if (!removeInMainteance) { // no removals are done in maintenance, since // removals are best effort let us try // to remove this node as well as other marked // deleted nodes connected to this node removeMaintLoopLockFree(prev, next, bottomStart.value); } else { // removals are done also in maintenance, so // just try to remove this node removeMaintLockFree(prev, next, bottomStart.value); } } return val; } } } else if (c < 0) return null; // Not at the correct node so continue traversal prev = getPrevNode(key, next, false); } } /** * * Traverses the skip-list for the node with the given key starting from the * given Index item, is "fast" because it does not keep track of the * locations from each level where the search moved down to the level below * * @param key * the key to search for * @param prev * the Index to start the search from * @param top * the level that is search started from * @param bottom * the bottom index level to stop on * @return the previous Node from the bottom level list of the key being * searched for */ private Node<K, V> getPrevFast(Comparable<? super K> key, Index<K, V> prev, int top, int bottom) { int c = 0; int nodesTraversed = 0; Index<K, V> next; for (;;) { // Inner for loop traverses a single Index list level for (;;) { next = prev.right; if (TRAVERSAL_COUNT) { nodesTraversed++; } if (next == null) // Reached the end break; K nextKey = next.node.key; if (nextKey == null) c = 1; else c = key.compareTo(nextKey); if (c <= 0) // Found a node with a bigger key break; prev = next; } if (next != null && c == 0) { // Immediately stop the Index traversal if a node with key was // found // Continue the traversal on the Node list level if (TRAVERSAL_COUNT) { finishCount1(nodesTraversed); } return getPrevNode(key, next.node.prev, false); } if (top == bottom) // Reached the bottom Index list level break; top--; next = prev.down; if (next == null) // Reached the bottom Index list level break; prev = next; if (TRAVERSAL_COUNT) { nodesTraversed++; } } if (TRAVERSAL_COUNT) { finishCount1(nodesTraversed); } // Continue the traversal on the Node list level return getPrevNode(key, prev.node, false); } /** * * Traverses the skip-list for the node with the given key starting from the * given Index item, is not "fast" because it keeps track of the locations * from each level where the search moved down to the level below in the * thdLocalPrevArray * * @param key * the key to search for * @param prev * the Index to start the search from * @param top * the level that is search started from * @param bottom * the bottom index level to stop on * @return the previous Node from the bottom level list of the key being * searched for */ private Node<K, V> getPrev(Comparable<? super K> key, Index<K, V> prev, int top, int bottom) { Index<K, V>[] array = thdLocalPrevArray.get(); for (;;) { for (;;) { Index<K, V> next = prev.right; if (next == null) break; int c; K nextKey = next.node.key; if (nextKey == null) c = 1; else c = key.compareTo(nextKey); if (c <= 0) break; prev = next; } // Store the location in the thdLocalPrevArray array[top] = prev; if (top == bottom) break; top--; prev = prev.down; } return getPrevNode(key, prev.node, true); } /** * Traverses the bottom list level looking for a node with key * * @param key * the key to search for * @param prev * the node to start the traversal from * @param isInsert * if this was called from within an insert operation * @return the node in the list previous to the one that has (or would have) * key */ private Node<K, V> getPrevNode(Comparable<? super K> key, Node<K, V> prev, boolean isInsert) { int c = 0; int nodesTraversed = 0; for (;;) { Node<K, V> next = prev.next; if (TRAVERSAL_COUNT) { nodesTraversed++; } if (next != null) { K nextKey = next.key; if (nextKey == null) c = 1; else c = key.compareTo(nextKey); } if (next == null || c <= 0) { // The reason for the additional check when doing an insert, is // that the previous node // must be physically in the list before performing adding a new // node after it if (isInsert) { if (prev.value == prev) { // the node has been removed, travel backwards until // back at a node not physically removed while (prev.value == prev) { prev = prev.prev; if (TRAVERSAL_COUNT) { nodesTraversed++; } } if (lockFree) { // In lock free need to help remove the node, to // ensure progress helpRemoval(prev, prev.next); } continue; } } if (lockFree) { helpRemoval(prev, prev.next); } if (TRAVERSAL_COUNT) { finishCount1(nodesTraversed); } return prev; } else { prev = next; } } } /** * Locks prev or neither, node should be passed in already locked * * @param prev * The node to lock * @param node * This node should already be locked * @return true if the nodes were locked successfully, false otherwise in * which case both nodes are unlocked */ private static final boolean lock(Node prev, Node node) { if (tryLock) { if (!prev.lock.tryLock()) { node.lock.unlock(); return false; } return true; } else { prev.lock.lock(); return true; } } /** * Locks a single node * * @param node * The node to lock * @return true if successful, false otherwise */ private static final boolean lockSingle(Node node) { if (tryLock) { if (!node.lock.tryLock()) { return false; } return true; } else { node.lock.lock(); return true; } } /** * Lock based physical removal (prev and node must be locked when this is * called) * * @param prev * the node in the list prior to the one to be removed * @param node * the node to be removed * @return true if successful, false otherwise */ private boolean doRemoval(Node<K, V> prev, Node<K, V> node) { // Ensure both the nodes haven't already been removed, that node is // marked deleted // and that prev is actually the node prior to node if (prev.value == prev || node.value == node || node.value != null || prev.next != node) { return false; } // physical removal Node<K, V> tmp = node.next; if (tmp != null) tmp.prev = prev; prev.next = tmp; node.value = (V) node; return true; } /** * Checks to see if the node has a 0 height so it can be removed * * @param node * The node to check * @param bottomLevel * The current bottom list level * @return true if node is safe for removal, false otherwise */ private static final boolean checkHeightRemoval(Node node, int bottomLevel) { return (node.vars.topLevel - 1 < bottomLevel); } /** * Tries to physically remove node * * @param prev * The node just prior to the node to be removed in the list * @param node * The node to be removed from the list, this node should be * locked * @return true if the removal was successful, false otherwise */ private boolean removeMaint(Node<K, V> prev, Node<K, V> node, int bottomLevel) { // Check to see if this is a valid node to remove if (node.value != null || !checkHeightRemoval(node, bottomLevel) || prev.value == prev || prev.next != node) { node.lock.unlock(); return false; } // lock the nodes if (!lock(prev, node)) return false; // perform the physical removal doRemoval(prev, node); if (STRUCT_MODS) { Vars vars = counts.get(); vars.structMods++; } prev.lock.unlock(); node.lock.unlock(); return true; } /** * Tries to physically remove node in a lock free manner * * @param prev * The node just prior to the node to be removed in the list * @param node * The node to be removed from the list * @return true if the removal was successful, false otherwise */ private boolean removeMaintLockFree(Node<K, V> prev, Node<K, V> node, int bottomLevel) { // Check to see if it is a valid node to remove if (node.value != null || !checkHeightRemoval(node, bottomLevel) || prev.value == prev || prev.next != node) { return false; } // Mark the node for removal node.casValue(null, node); if (node.value != node) { return false; } // Finish the removal if (!helpRemoval(prev, node)) return false; // Update the prev pointer // Synchronization not used here as prev pointer just needs to point // back to the // list, not the exact prev node Node<K, V> prevNext = prev.next; if (prevNext != null) prevNext.prev = prev; if (STRUCT_MODS) { Vars vars = counts.get(); vars.structMods++; } return true; } /** * Tries to physically remove node in a lock free manner, plus any marked * nodes immediately previous to this node * * @param prev * The node just prior to the node to be removed in the list * @param node * The node to be removed from the list * @return true */ private boolean removeMaintLoopLockFree(Node<K, V> prev, Node<K, V> node, int bottomLevel) { while (removeMaintLockFree(prev, node, bottomLevel)) { node = prev; prev = prev.prev; } return true; } /** * Tries to finish the removal of a node who has been marked for removal in * a lock-free manner * * @param prev * The node just prior to the node to be removed in the list * @param node * The node to be removed from the list * @return true if successful, false otherwise */ public boolean helpRemoval(Node<K, V> prev, Node<K, V> node) { Node<K, V> next; // Ensure the previous node is not a marker if (prev.key == null || prev.next != node) return false; // Ensure the node is not a marker if (node == null || node.value != node) return false; next = node.next; while (next == null || next.key != null) { // Insert a marker after the node node.casNext(next, Node.newMarker(node, next)); next = node.next; } // remove the node and the marker if (prev.casNext(node, next.next)) return true; return false; } /** * Loops, physically removing nodes until a removal fails, starts at node * moving backwards in the list. Only removes nodes with height 0 that are * marked as deleted. Does hand over hand locking. * * @param prev * The node just prior to the node to be removed in the list * @param node * The node to be removed from the list, this node should be * locked * @return true if the removal was successful, false otherwise */ private boolean removeMaintLoop(Node<K, V> prev, Node<K, V> node, int bottomLevel) { // ensure the node is correct for removal if (node.value != null || !checkHeightRemoval(node, bottomLevel) || prev.value == prev || prev.next != node) { node.lock.unlock(); return false; } if (!lock(prev, node)) return false; // loop backwards removing nodes until failed while (true) { if (doRemoval(prev, node)) { node.lock.unlock(); if (STRUCT_MODS) { Vars vars = counts.get(); vars.structMods++; } node = prev; prev = prev.prev; // check the node prior is ok for removal if (node.value != null || !checkHeightRemoval(node, bottomLevel) || prev.value == prev || prev.next != node) { node.lock.unlock(); return true; } if (!lock(prev, node)) return true; } else { break; } } prev.lock.unlock(); node.lock.unlock(); return true; } /** * Does a full traversal of the list, attempting to remove nodes of height 0 * that are marked deleted * * No longer used! */ private void removeTraversal() { Node<K, V> prev, next; int bottomLevel = bottomStart.value; prev = begin; next = prev.next; while (next != null) { if (checkHeightRemoval(next, bottomLevel) && next.value == null) { if (!lockFree) { if (lockSingle(next)) { removeMaint(prev, next, bottomLevel); } } else { removeMaintLockFree(prev, next, bottomLevel); } } prev = next; next = next.next; } } /** * Checks to see if the bottom level should be raised, happens based on some * function of the number of tall, marked deleted nodes and the total number * of nodes, this is called by the maintenance thread * * @return true if the bottom level should be raised, false otherwise */ private final boolean checkShouldRaiseBottomLevelMaint(MaintVars vars) { if (vars.tallDeletedCount > vars.nonDeleted * 10) return true; return false; } /** * Checks to see if the bottom level should be raised and does so if * necessary, happens based on some function of the number of tall, marked * deleted nodes, this is called by any thread * * No longer used! * * @return true if the bottom level was raised, false otherwise */ private final boolean checkShouldRaiseBottomLevel(HeadPointer<K, V> bottom) { if (s_random.get().nextInt(10000) < 9999) return false; MaintVars vars = new MaintVars(); Node<K, V> next = begin.next; vars.tallDeletedCount = 0; vars.totalCount = 0; vars.nonDeleted = 0; int bottomLevel = bottom.value; while (next != null) { if (next.value == null && !checkHeightRemoval(next, bottomLevel)) { vars.tallDeletedCount++; } if (next.value != next && next.value != null) { vars.nonDeleted++; } vars.totalCount++; next = next.next; } if (checkShouldRaiseBottomLevelMaint(vars)) { this.increaseBottomStart(bottom); return true; } return false; } /** * Does a full traversal of the skip list "Mixed" because is does all the * following: removing nodes, raising node levels, and removing bottom Index * level if conditions are met */ private void mixedTraversal() { HeadPointer<K, V> bottom = bottomStart, top = topStart; // Check to see if the bottom Index level should be removed (i.e. there // are too many tall, marked deleted nodes) if ((removeInDelete || removeInMainteance) && doBottomLevelRaises && checkShouldRaiseBottomLevelMaint(vars)) { // Remove the bottom index level increaseBottomStart(bottom); bottom = bottomStart; } // Traverse the Node list boolean status = nodeLevelTraversal(bottom); Index<K, V>[] array = thdLocalPrevArray.get(); // Get the first Index item in ever list initializeIndexArray(top, bottom); int currentLevel = bottom.value; // Traverse each index level until nodes don't need to be made any // higher while (status) { // First make sure we have an index level above if (top.value <= currentLevel + 1) { // if not, add another level if (!increaseTopStart(top)) break; top = topStart; array[top.value] = top.node; } // Traverse the index level, raising status = indexLevelTraversal(array[currentLevel + 1], array[currentLevel], currentLevel); currentLevel++; } } /** * Checks if a node should have it's level raised * * @param node * The node to check * @param bottomLevel * The level to check * @return true if the node should raise its level, false otherwise */ private static final boolean checkShouldRaiseNode(Node node, int bottomLevel) { Node prev = node.prev, next = node.next; if (prev == null || next == null) return false; // Check the heights of the neighboring nodes, only raise the height if // both its neighbors have height of 1 if (!(prev.vars.topLevel - 1 < bottomLevel && next.vars.topLevel - 1 < bottomLevel && node.vars.topLevel - 1 < bottomLevel)) return false; if (maintenance) { return true; } else { // In the case of no maintenance, only raise nodes if additionally // its neighbors neighbors have height 1 prev = prev.prev; next = next.next; if (prev == null || next == null) return false; return (prev.vars.topLevel - 1 < bottomLevel && next.vars.topLevel - 1 < bottomLevel); } } /** * Checks if a node should have it's level raised, looking from an index * level * * @param prevIndex * The index prior to this node in the list * @param nodeIndex * The node to check * @param bottomLevel * The level to check * @return true if the node should raise its level, false otherwise */ private static final boolean checkShouldRaiseIndex(Index prevIndex, Index nodeIndex, int bottomLevel) { Index nextIndex = nodeIndex.right; if (prevIndex == null || nextIndex == null) return false; Node prev = prevIndex.node, next = nextIndex.node; return (prev.vars.topLevel - 1 <= bottomLevel && next.vars.topLevel - 1 <= bottomLevel && nodeIndex.node.vars.topLevel - 1 <= bottomLevel); } /** * Traverses the given index leveling raising items up one level if needed * * @param above * The start of the upper level list * @param current * The start of the lower level list * @param currentLevel * The number of the current level * @return true if the level should be raised again, false otherwise */ private boolean indexLevelTraversal(Index<K, V> above, Index<K, V> current, int currentLevel) { boolean raised = false; Index<K, V> prevAbove = above, prev = current, next = current.right; Node<K, V> nextNode; if (prevAbove == null) // Should not happen System.out.println("SSSSSSSS" + currentLevel); // Traverse the level while (next != null) { nextNode = next.node; // Ensure the node should be raised if (nextNode.value != nextNode && checkShouldRaiseIndex(prev, next, currentLevel)) { // Raise the node prevAbove = raiseSingleListLevel(comparable(nextNode.key), prevAbove, next, nextNode); nextNode.vars.topLevel++; raised = true; } prev = next; next = next.right; } return raised; } /** * Traverses the bottom level of the skip list, removing nodes and raising * the level of nodes by one if necessary * * @param bottom * The start of the bottom index level * @return true if the levels should be raised again, false otherwise */ private boolean nodeLevelTraversal(HeadPointer<K, V> bottom) { Index<K, V> prevIndex = bottom.node; Node<K, V> prev = begin, next = begin.next; int bottomLevel = bottom.value; boolean raised = false; vars.tallDeletedCount = 0; vars.totalCount = 0; vars.nonDeleted = 0; while (next != null) { // check if marked deleted if (next.value == null) { // Check for removal if (checkHeightRemoval(next, bottomLevel)) { if (removeInMainteance) { if (!lockFree) { if (next.value != next && lockSingle(next)) { if (removeMaint(prev, next, bottomLevel)) vars.removals++; } } else { if (removeMaintLockFree(prev, next, bottomLevel)) vars.removals++; } } } else { // Number of marked deleted nodes vars.tallDeletedCount++; } } // if not marked deleted, check if it should be raised else if (next.value != next && checkShouldRaiseNode(next, bottomLevel)) { prevIndex = raiseSingleListLevel(comparable(next.key), prevIndex, null, next); next.vars.topLevel = bottomLevel + 1; raised = true; } if (next.value != next && next.value != null) { // number of nodes not marked deleted vars.nonDeleted++; } // total number of nodes vars.totalCount++; prev = next; next = next.next; } return raised; } /** * Does a full traversal of the list, setting the level of newly inserted * nodes * * No longer used! */ private void raiseTraversal() { HeadPointer<K, V> top = topStart, bottom = bottomStart; initializeIndexArray(top, bottom); Node<K, V> next = begin.next; while (next != null) { if (!next.vars.updated && next.value != null) { raiseLevels(comparable(next.key), next, top, bottom.value); next.vars.updated = true; } next = next.next; } } /** * Returns a random level for inserting a new node. Hardwired to k=1, p=0.5, * max 31 (see above and Pugh's "Skip List Cookbook", sec 3.4). * * This uses the simplest of the generators described in George Marsaglia's * "Xorshift RNGs" paper. This is not a high-quality generator but is * acceptable here. */ private int randomLevel() { return Math.min((totalHeight - 1), (skiplists.RandomLevelGenerator.randomLevel())); } /** * Raises a newly created node up to some random list level, called by the * insert operation This is only done when there is no maintenance * * @param key * The comparable key * @param node * The newly created node * @param top * The maximum list level to go to * @param bottom * The bottom list level to start from */ private void raiseLevels(Comparable<? super K> key, Node<K, V> node, HeadPointer<K, V> top, int bottom) { if (!checkShouldRaiseNode(node, bottom)) return; int height = randomLevel(); height--; int maxHeight = top.value - bottom + 1; if (height > maxHeight) { increaseTopStart(top); } height = Math.min(height, top.value - bottom + 1); if (height > 0) { // Do the raising node.vars.topLevel = bottom + height; Index<K, V> prevIndex = null; // The thdLocalPrevArray has already been filled during the // traversal of the insert operation Index<K, V>[] array = thdLocalPrevArray.get(); while (top.value >= bottom && height > 0) { prevIndex = raiseSingleListLevel(key, array[bottom], prevIndex, node); bottom++; height--; } } } /** * Increases the value of top start by 1 if the head pointer prev has not * changed, this is to add a new Index to the top of the skip list * * @param prev * The previous top head pointer * @return true if successful, false otherwise */ private boolean increaseTopStart(HeadPointer<K, V> prev) { if (topStart == prev && prev.value + 1 < totalHeight) { return casTopStart(prev, new HeadPointer<K, V>(new Index<K, V>( begin, prev.node, null), prev.value + 1)); } return false; } /** * Raises the level of the bottom level by one This is to remove the Index * level at the bottom * * @param prev * The previous top head pointer * @return true if successful, false otherwise */ private boolean increaseBottomStart(HeadPointer<K, V> prev) { HeadPointer<K, V> top = topStart; if (bottomStart == prev && top.value > prev.value) { Index<K, V> nextIndex, prevIndex; nextIndex = top.node; prevIndex = top.node; // Find the Index level that is just above the one to get rid of while (nextIndex != prev.node) { prevIndex = nextIndex; nextIndex = nextIndex.down; } if (casBottomStart(prev, new HeadPointer<K, V>(prevIndex, prev.value + 1))) { bottomLevelRaiseCount.getAndIncrement(); return true; } } return false; } /** * Raises the level of the node up a single list level * * @param prev * The expected previous item in the list * @param down * The list item directly below this * @param node * The node for the list items */ private Index<K, V> raiseSingleListLevel(Comparable<? super K> key, Index<K, V> prev, Index<K, V> down, Node<K, V> node) { // Allocate a new Item for this node Index<K, V> item = new Index<K, V>(node, down, null); if (prev == null) // Should not happen System.out.println("!!!!!!!!!!!!!!!!!!!!!!"); // Loop until you find the previous Index item in the list for (;;) { Index<K, V> next = prev.right; int c = -1; if (next != null) { c = key.compareTo(next.node.key); } if (c < 0) { // Insert the new Item item.right = next; if (prev.casRight(next, item)) return item; } else { prev = next; } } } /** * Initialize the maintenance thread's index array. Fills the array with the * index values from the beginning of the list * * @param top * The index level to start at * @param bottom * The index level to end at */ private void initializeIndexArray(HeadPointer<K, V> top, HeadPointer<K, V> bottom) { Index<K, V>[] array = thdLocalPrevArray.get(); int topValue = top.value, bottomValue = bottom.value; Index<K, V> next = top.node; for (;;) { array[topValue] = next; if (topValue == bottomValue) { if (next != bottomStart.node) System.out.println("ERERERER!!!!!!!!!"); break; } topValue--; next = next.down; } } /** * After a modification, should an application thread try to perform * maintenance * * No longer used! */ private void checkMaint() { if (s_random.get().nextDouble() <= this.maintPercentage) { if (this.maintLock.tryLock()) { // this.doMaintenance(true); mixedTraversal(); this.maintLock.unlock(); } } } void finishCount(int nodesTraversed) { Vars vars = counts.get(); vars.getCount++; vars.nodesTraversed += nodesTraversed; } /** * Starts the maintenance thread * * @return true */ public boolean startMaintenance() { if (this.seperateMaint) { stop = false; // If no mainteance thread, start a new one if (mainThd.get() == null) { if (mainThd.compareAndSet(null, new MaintenanceThread())) { mainThd.get().start(); } } } return true; } /** * Stops the maintenance thread, does not return until the thread actually * stops * * @return true */ public boolean stopMaintenance() { if (maintenance) { if (seperateMaint) { stop = true; MaintenanceThread thd = mainThd.get(); if (thd != null) { try { // Wait for maintenance to stop thd.join(); } catch (InterruptedException e) { e.printStackTrace(); } // Get rid of the mainteance thread mainThd.compareAndSet(thd, null); } // Remove this skip list from the ones that will be maintained skipLists.remove(this); } System.out .println("Height Changes: " + bottomLevelRaiseCount.get()); } return true; } /** * Loop performing maintenance on all the skip lists until stopped */ public static void doMaintenance() { while (!stop) { // Go through each skip list in the ones to be maintained, // performing a mixed traversal Object[] arry = (Object[]) skipLists.toArray(); for (int i = 0; i < arry.length; i++) { ((NonBlockingFriendlySkipListMap) arry[i]).mixedTraversal(); } } if (STRUCT_MODS) { Object[] arry = (Object[]) skipLists.toArray(); for (int i = 0; i < arry.length; i++) { ((NonBlockingFriendlySkipListMap) arry[i]).vars.removals = counts .get().structMods; } } } @Override public void clear() { this.stopMaintenance(); this.resetSkipList(); this.startMaintenance(); return; } /** * Re-initialize the skip list and resets all counts */ public void resetSkipList() { initialize(); this.bottomLevelRaiseCount.set(0); this.vars.heightChanges = 0; this.vars.removals = 0; this.vars.tallDeletedCount = 0; this.vars.totalCount = 0; this.vars.nonDeleted = 0; } public int getBottomLevelRaiseCount() { return this.bottomLevelRaiseCount.get(); } /** * Counts the total number of nodes in the skip list * * @return the number of nodes */ public int numNodes() { int count = 0; Node<K, V> current = begin.next; while (current != null) { count++; current = current.next; } System.out.println(); return count; } @Override public int size() { int count = 0; Node<K, V> current = begin.next; while (current != null) { if (current.value != null) { count++; } current = current.next; } return count; } public long getStructMods() { return vars.removals; } @Override public Set<java.util.Map.Entry<K, V>> entrySet() { // TODO Auto-generated method stub return null; } /******************** Iterator stuff ********************/ @Override public K firstKey() { Node<K, V> n = findFirst(); if (n == null) throw new NoSuchElementException(); return n.key; } @Override public boolean isEmpty() { return findFirst() == null; } Node<K, V> findFirst() { Node<K, V> n = begin; for (;;) { n = n.next; if (n == null) return null; if (n.value != null && n.value != n) return n; } } /** * Base of iterator classes: */ abstract class Iter<T> implements Iterator<T> { /** the last node returned by next() */ Node<K, V> lastReturned; /** the next node to return from next(); */ Node<K, V> next; /** Cache of next value field to maintain weak consistency */ V nextValue; /** Initializes ascending iterator for entire range. */ Iter() { for (;;) { next = findFirst(); if (next == null) break; Object x = next.value; if (x != null && x != next) { nextValue = (V) x; break; } } } public final boolean hasNext() { return next != null; } /** Advances next to higher entry. */ final void advance() { if (next == null) throw new NoSuchElementException(); lastReturned = next; for (;;) { next = next.next; if (next == null) break; Object x = next.value; if (x != null && x != next) { nextValue = (V) x; break; } } } public void remove() { Node<K, V> l = lastReturned; if (l == null) throw new IllegalStateException(); // It would not be worth all of the overhead to directly // unlink from here. Using remove is fast enough. NonBlockingFriendlySkipListMap.this.remove(l.key); lastReturned = null; } } Iterator<V> valueIterator() { return new ValueIterator(); } final class ValueIterator extends Iter<V> { public V next() { V v = nextValue; advance(); return v; } } /** Lazily initialized values collection */ private transient Values values; public Collection<V> values() { Values vs = values; return (vs != null) ? vs : (values = new Values(this)); } static final class Values<E> extends AbstractCollection<E> { private final ConcurrentNavigableMap<Object, E> m; Values(ConcurrentNavigableMap<Object, E> map) { m = map; } public Iterator<E> iterator() { return ((NonBlockingFriendlySkipListMap<Object, E>) m) .valueIterator(); } public boolean isEmpty() { return m.isEmpty(); } public int size() { return m.size(); } public boolean contains(Object o) { return m.containsValue(o); } public void clear() { m.clear(); } public Object[] toArray() { // TODO Auto-generated method stub // return toList(this).toArray(); return null; } public <T> T[] toArray(T[] a) { // TODO Auto-generated method stub // return toList(this).toArray(a); return null; } } @Override public boolean remove(Object arg0, Object arg1) { // TODO Auto-generated method stub return false; } @Override public V replace(K arg0, V arg1) { // TODO Auto-generated method stub return null; } @Override public boolean replace(K arg0, V arg1, V arg2) { // TODO Auto-generated method stub return false; } @Override public java.util.Map.Entry<K, V> ceilingEntry(K arg0) { // TODO Auto-generated method stub return null; } @Override public K ceilingKey(K arg0) { // TODO Auto-generated method stub return null; } @Override public java.util.Map.Entry<K, V> firstEntry() { // TODO Auto-generated method stub return null; } @Override public java.util.Map.Entry<K, V> floorEntry(K arg0) { // TODO Auto-generated method stub return null; } @Override public K floorKey(K arg0) { // TODO Auto-generated method stub return null; } @Override public java.util.Map.Entry<K, V> higherEntry(K arg0) { // TODO Auto-generated method stub return null; } @Override public K higherKey(K arg0) { // TODO Auto-generated method stub return null; } @Override public java.util.Map.Entry<K, V> lastEntry() { // TODO Auto-generated method stub return null; } @Override public java.util.Map.Entry<K, V> lowerEntry(K arg0) { // TODO Auto-generated method stub return null; } @Override public K lowerKey(K arg0) { // TODO Auto-generated method stub return null; } @Override public java.util.Map.Entry<K, V> pollFirstEntry() { // TODO Auto-generated method stub return null; } @Override public java.util.Map.Entry<K, V> pollLastEntry() { // TODO Auto-generated method stub return null; } @Override public Comparator<? super K> comparator() { // TODO Auto-generated method stub return null; } @Override public K lastKey() { // TODO Auto-generated method stub return null; } @Override public NavigableSet<K> descendingKeySet() { // TODO Auto-generated method stub return null; } @Override public ConcurrentNavigableMap<K, V> descendingMap() { // TODO Auto-generated method stub return null; } @Override public ConcurrentNavigableMap<K, V> headMap(K toKey) { // TODO Auto-generated method stub return null; } @Override public ConcurrentNavigableMap<K, V> headMap(K toKey, boolean inclusive) { // TODO Auto-generated method stub return null; } @Override public NavigableSet<K> keySet() { // TODO Auto-generated method stub return null; } @Override public NavigableSet<K> navigableKeySet() { // TODO Auto-generated method stub return null; } @Override public ConcurrentNavigableMap<K, V> subMap(K fromKey, K toKey) { // TODO Auto-generated method stub return null; } @Override public ConcurrentNavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { // TODO Auto-generated method stub return null; } @Override public ConcurrentNavigableMap<K, V> tailMap(K fromKey) { // TODO Auto-generated method stub return null; } @Override public ConcurrentNavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { // TODO Auto-generated method stub return null; } }