package trees.transactional; import java.util.Collection; import java.util.Map; import java.util.Random; import java.util.Set; import org.deuce.Atomic; import contention.abstractions.CompositionalIntSet; import contention.abstractions.CompositionalMap; import contention.abstractions.MaintenanceAlg; /** * A Java implementation of the Speculation-friendly binary search tree * as described in: * * T. Crain, V. Gramoli and M.Raynal. A Speculation-friendly Binary * Search Tree. PPoPP 2012. * * @author Tyler Crain * */ public class TransactionalFriendlyTreeSet<K, V> implements CompositionalIntSet, CompositionalMap<K, V>, MaintenanceAlg { /** The thread-private PRNG */ final private static ThreadLocal<Random> s_random = new ThreadLocal<Random>() { @Override protected synchronized Random initialValue() { return new Random(); } }; private class MaintenanceThread extends Thread { TransactionalFriendlyTreeSet map; MaintenanceThread(TransactionalFriendlyTreeSet map) { this.map = map; } public void run() { map.doMaintenance(); } } private static class Node { int key; int localh, lefth, righth; Node left; Node right; boolean deleted; Node(final int key) { this.key = key; this.deleted = false; this.right = null; this.left = null; this.localh = 1; this.righth = 0; this.lefth = 0; } Node(final int key, final int localh, final int lefth, final int righth, final boolean deleted, final int value, final Node left, final Node right) { this.key = key; this.localh = localh; this.righth = righth; this.lefth = lefth; this.deleted = deleted; this.left = left; this.right = right; } void setupNode(final int key, final int localh, final int lefth, final int righth, final boolean deleted, final Node left, final Node right) { this.key = key; this.localh = localh; this.righth = righth; this.lefth = lefth; this.deleted = deleted; this.left = left; this.right = right; } void updateLocalh() { this.localh = Math.max(this.lefth + 1, this.righth + 1); } } // state private final Node root = new Node(Integer.MAX_VALUE); volatile boolean stop = false; private final MaintenanceThread mainThd = new MaintenanceThread(this); private long rotations = 0, propogations = 0; // Constructors public TransactionalFriendlyTreeSet() { // temporary this.startMaintenance(); } @Override public void fill(final int range, final long size) { while (this.size() < size) { this.addInt(s_random.get().nextInt(range)); } } @Override @Atomic public boolean addInt(int x) { Node next = this.root, prev = this.root; while (next != null) { if (next.key > x) { prev = next; next = next.left; } else if (next.key < x) { prev = next; next = next.right; } else { if (next.deleted) { next.deleted = false; return true; } else { return false; } } } Node newNode = new Node(x); if (prev.key > x) { prev.left = newNode; } else if (prev.key < x) { prev.right = newNode; } return true; } @Override @Atomic public boolean removeInt(int x) { Node next = this.root; while (next != null) { if (next.key > x) { next = next.left; } else if (next.key < x) { next = next.right; } else { if (next.deleted) { return false; } else { next.deleted = true; return true; } } } return false; } @Override @Atomic public boolean containsInt(int x) { Node next = this.root; while (next != null) { if (next.key > x) { next = next.left; } else if (next.key < x) { next = next.right; } else { if (next.deleted) { return false; } else { return true; } } } return false; } @Override public Object getInt(int x) { if (containsInt(x)) return x; else return null; } @Override @Atomic public boolean addAll(Collection<Integer> c) { boolean result = true; for (int x : c) result &= this.addInt(x); return result; } @Override @Atomic public boolean removeAll(Collection<Integer> c) { boolean result = true; for (int x : c) if (!this.removeInt(x)) result = false; return result; } @Atomic(metainf = "maint") private boolean removeNode(Node parent, boolean leftChild) { Node node, child; if (leftChild) { node = parent.left; } else { node = parent.right; } if (node == null) { return false; } if (!node.deleted) { return false; } child = node.right; if (child == null) { child = node.left; } else { if (node.left != null) return false; } if (leftChild) { parent.left = child; } else { parent.right = child; } return true; } @Atomic(metainf = "maint") private Node getChild(Node node, boolean leftChild) { if (leftChild) return node.left; return node.right; } private void recTraverseTree(Node node) { Node lChild, rChild; if (node == null) return; if (removeNode(node, true)) return; if (removeNode(node, false)) return; lChild = getChild(node, true); recTraverseTree(lChild); rChild = getChild(node, false); recTraverseTree(rChild); if (lChild != null) { if (propagate(lChild)) { this.performRotation(node, true); } } if (rChild != null) { if (propagate(rChild)) { this.performRotation(node, false); } } } @Atomic(metainf = "maint") int rightRotate(Node parent, boolean leftChild, boolean doRotate) { Node n, l, lr; n = leftChild ? parent.left : parent.right; if (n == null) return 0; l = n.left; if (l == null) return 0; if (l.lefth - l.righth < 0 && !doRotate) { // should do a double rotate return 2; } lr = l.right; l.right = n; n.left = lr; n.lefth = l.righth; n.updateLocalh(); l.righth = n.localh; l.updateLocalh(); if (leftChild) { parent.left = l; } else { parent.right = l; } // need to update balance values if (leftChild) { parent.lefth = l.localh; } else { parent.righth = l.localh; } parent.updateLocalh(); // System.out.println("right rotate"); rotations++; return 1; } @Atomic(metainf = "maint") int leftRotate(Node parent, boolean leftChild, boolean doRotate) { Node n, r, rl; n = leftChild ? parent.left : parent.right; if (n == null) return 0; r = n.right; if (r == null) return 0; if (r.lefth - r.righth > 0 && !doRotate) { // should do a double rotate return 3; } rl = r.left; r.left = n; n.right = rl; n.righth = r.lefth; n.updateLocalh(); r.lefth = n.localh; r.updateLocalh(); if (leftChild) { parent.left = r; } else { parent.right = r; } // need to update balance values if (leftChild) { parent.lefth = r.localh; } else { parent.righth = r.localh; } parent.updateLocalh(); // System.out.println("left rotate"); rotations++; return 1; } boolean propagate(Node node) { Node lchild, rchild; lchild = getChild(node, true); rchild = getChild(node, false); if (lchild == null) { node.lefth = 0; } else { node.lefth = lchild.localh; } if (rchild == null) { node.righth = 0; } else { node.righth = rchild.localh; } node.updateLocalh(); propogations++; if (Math.abs(node.righth - node.lefth) >= 2) return true; return false; } boolean performRotation(Node parent, boolean goLeft) { int ret; Node node; ret = singleRotation(parent, goLeft, false, false); if (ret == 2) { // Do a LRR node = goLeft ? getChild(parent, true) : getChild(parent, false); ret = singleRotation(node, true, true, false); if (ret > 0) { if (singleRotation(parent, goLeft, false, true) > 0) { // System.out.println("LRR"); } } } else if (ret == 3) { // Do a RLR node = goLeft ? getChild(parent, true) : getChild(parent, false); ret = singleRotation(node, false, false, true); if (ret > 0) { if (singleRotation(parent, goLeft, true, false) > 0) { // System.out.println("RLR"); } } } if (ret > 0) return true; return false; } int singleRotation(Node parent, boolean goLeft, boolean leftRotation, boolean rightRotation) { int bal, ret = 0; Node node, child; node = goLeft ? parent.left : parent.right; bal = node.lefth - node.righth; if (bal >= 2 || rightRotation) { // check reiable and rotate child = getChild(node, true); if (child != null) { if (node.lefth == child.localh) { ret = rightRotate(parent, goLeft, rightRotation); } } } else if (bal <= -2 || leftRotation) { // check reliable and rotate child = getChild(node, false); if (child != null) { if (node.righth == child.localh) { ret = leftRotate(parent, goLeft, leftRotation); } } } return ret; } @Override @Atomic public int size() { return recSize(root.left); } @Atomic public int totalSize() { return recTotalSize(root.left); } public int recTotalSize(Node node) { if (node == null) return 0; int size = 0; size += recTotalSize(node.left); size += recTotalSize(node.right); size++; return size; } public int recSize(Node node) { if (node == null) return 0; int size = 0; size += recSize(node.left); size += recSize(node.right); if (!node.deleted) { size++; } return size; } @Override public void clear() { } // @Override // @Atomic // public Object putIfAbsent(int x, int y) { // if (!contains(y)) // add(x); // return null; // } public boolean stopMaintenance() { this.stop = true; try { this.mainThd.join(); } catch (InterruptedException e) { e.printStackTrace(); } return true; } public boolean startMaintenance() { this.mainThd.start(); return true; } boolean doMaintenance() { while (!stop) { recTraverseTree(root); } System.out.println("Rotations: " + rotations); System.out.println("Propogations: " + propogations); return true; } @Override public long getStructMods() { // TODO Auto-generated method stub return 0; } @Override public int numNodes() { return this.totalSize(); } @Override @Atomic(metainf = "regular") public V putIfAbsent(K x, V y) { if (!containsInt((Integer) y)) addInt((Integer) x); return null; } @Override @Atomic(metainf = "regular") public Object putIfAbsent(int x, int y) { if (!containsInt(y)) addInt(x); return null; } @Override public V get(Object key) { if (containsInt((Integer) key)) return (V) key; else return null; } // @Override // public V putIfAbsent(K k, V v) { // // TODO Auto-generated method stub // return null; // } @Override public V remove(Object key) { if (removeInt((Integer) key)) return (V) key; else return null; } @Override public boolean containsKey(Object key) { return containsInt((Integer) key); } @Override public boolean containsValue(Object value) { // TODO Auto-generated method stub return false; } @Override public Set<java.util.Map.Entry<K, V>> entrySet() { // TODO Auto-generated method stub return null; } @Override public boolean isEmpty() { // TODO Auto-generated method stub return false; } @Override public Set<K> keySet() { // TODO Auto-generated method stub return null; } @Override public V put(K key, V value) { // TODO Auto-generated method stub return null; } @Override public void putAll(Map<? extends K, ? extends V> m) { // TODO Auto-generated method stub } @Override public Collection<V> values() { // TODO Auto-generated method stub return null; } }