package trees.lockbased; import contention.abstractions.CompositionalMap; import contention.abstractions.MaintenanceAlg; import sun.misc.Unsafe; import java.lang.reflect.Constructor; import java.util.AbstractMap; import java.util.Comparator; import java.util.Set; /** * Created by vaksenov on 16.09.2016. */ public class ConcurrencyOptimalTreeMap<K, V> extends AbstractMap<K, V> implements CompositionalMap<K, V>, MaintenanceAlg { private static final Unsafe unsafe; private static final long stateStampOffset, leftStampOffset, rightStampOffset, valueOffset; static { try { Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor(); unsafeConstructor.setAccessible(true); unsafe = unsafeConstructor.newInstance(); stateStampOffset = unsafe.objectFieldOffset(ConcurrencyOptimalTreeMap.Node.class.getDeclaredField("stateStamp")); leftStampOffset = unsafe.objectFieldOffset(ConcurrencyOptimalTreeMap.Node.class.getDeclaredField("lStamp")); rightStampOffset = unsafe.objectFieldOffset(ConcurrencyOptimalTreeMap.Node.class.getDeclaredField("rStamp")); valueOffset = unsafe.objectFieldOffset(ConcurrencyOptimalTreeMap.Node.class.getDeclaredField("value")); } catch (Exception e) { throw new Error(e); } } public class Node { final K key; volatile V value; volatile boolean deleted; volatile int stateStamp = 0; volatile Node l; volatile int lStamp = 0; volatile Node r; volatile int rStamp = 0; public Node(K key, V value) { this.key = key; this.value = value; deleted = false; l = null; r = null; } public boolean equals(Object o) { if (!(o instanceof ConcurrencyOptimalTreeMap.Node)) { return false; } Node node = (Node) o; return node.key == key; } public int numberOfChildren() { return (l == null ? 0 : 1) + (r == null ? 0 : 1); } private boolean compareAndSetStateStamp(int expected, int updated) { return unsafe.compareAndSwapInt(this, stateStampOffset, expected, updated); } private boolean compareAndSetLeftStamp(int expected, int updated) { return unsafe.compareAndSwapInt(this, leftStampOffset, expected, updated); } private boolean compareAndSetRightStamp(int expected, int updated) { return unsafe.compareAndSwapInt(this, rightStampOffset, expected, updated); } private boolean compareAndSetValue(V expected, V updated) { if (updated == value) { return true; } return unsafe.compareAndSwapObject(this, valueOffset, expected, updated); } public V setAndGet(V set) { V value; do { value = this.value; } while (!compareAndSetValue(value, set)); return value; } public V setAndGetNotNull(V set) { V value; do { value = this.value; } while (value != null && !compareAndSetValue(value, set)); return value; } public void readLockLeft() { int stamp; while (true) { stamp = this.lStamp; if (stamp == 1) { continue; } if (compareAndSetLeftStamp(stamp, stamp + 2)) break; } } public void readLockRight() { int stamp; while (true) { stamp = this.rStamp; if (stamp == 1) { continue; } if (compareAndSetRightStamp(stamp, stamp + 2)) break; } } public void readLockState() { int stamp; while (true) { stamp = this.stateStamp; if (stamp == 1) { continue; } if (compareAndSetStateStamp(stamp, stamp + 2)) break; } } public void writeLockLeft() { while (true) { if (lStamp != 0) { continue; } if (compareAndSetLeftStamp(0, 1)) break; } } public void writeLockRight() { while (true) { if (rStamp != 0) { continue; } if (compareAndSetRightStamp(0, 1)) break; } } public void writeLockState() { while (true) { if (stateStamp != 0) { continue; } if (compareAndSetStateStamp(0, 1)) break; } } public boolean tryWriteLockWithConditionRefLeft(Node expected) { Node value = l; if (expected != value || lStamp != 0) { return false; } if (compareAndSetLeftStamp(0, 1)) { if (expected != l) { unlockWriteLeft(); return false; } return true; } else { return false; } } public boolean tryWriteLockWithConditionValLeft(Node expected) { Node value = l; if (lStamp != 0 || (value == null || compare(expected.key, value.key) != 0)) { return false; } if (compareAndSetLeftStamp(0, 1)) { if (l == null || compare(expected.key, l.key) != 0) { unlockWriteLeft(); return false; } return true; } else { return false; } } public boolean tryWriteLockWithConditionRefRight(Node expected) { Node value = r; if (expected != value || rStamp != 0) { return false; } if (compareAndSetRightStamp(0, 1)) { if (expected != r) { unlockWriteRight(); return false; } return true; } else { return false; } } public boolean tryWriteLockWithConditionValRight(Node expected) { Node value = r; if (rStamp != 0 || (value == null || compare(expected.key, value.key) != 0)) { return false; } if (compareAndSetRightStamp(0, 1)) { if (r == null || compare(expected.key, r.key) != 0) { unlockWriteRight(); return false; } return true; } else { return false; } } private boolean dataMatch(V value, boolean data) { return data ^ (value == null); } public boolean tryWriteLockWithConditionState(boolean data) { V value = this.value; if (stateStamp != 0 || !dataMatch(value, data) || deleted) { return false; } if (compareAndSetStateStamp(0, 1)) { if (!dataMatch(this.value, data) || deleted) { unlockWriteState(); return false; } return true; } else { return false; } } public boolean tryReadLockWithConditionState(boolean data) { int stamp; V value; while (true) { stamp = this.stateStamp; value = this.value; if (stamp == 1 || !dataMatch(value, data) || deleted) { return false; } if (compareAndSetStateStamp(stamp, stamp + 2)) { if (!dataMatch(this.value, data) || deleted) { unlockReadState(); } else { return true; } } } } public void unlockReadLeft() { int stamp; while (true) { stamp = this.lStamp; if (compareAndSetLeftStamp(stamp, stamp - 2)) break; } } public void unlockReadRight() { int stamp; while (true) { stamp = this.rStamp; if (compareAndSetRightStamp(stamp, stamp - 2)) break; } } public void unlockReadState() { int stamp; while (true) { stamp = this.stateStamp; if (compareAndSetStateStamp(stamp, stamp - 2)) break; } } public void unlockWriteLeft() { lStamp = 0; } public void unlockWriteRight() { rStamp = 0; } public void unlockWriteState() { stateStamp = 0; } public void multiUnlockState() { int stamp = this.stateStamp; if (stamp == 1) { stateStamp = 0; } else { while (!compareAndSetStateStamp(stamp, stamp - 2)) { stamp = this.stateStamp; } } } } private final Node ROOT = new Node(null, null); private Comparator<? super K> comparator; public ConcurrencyOptimalTreeMap() { } public ConcurrencyOptimalTreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } private 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); } }; } private int compare(final K k1, final K k2) { if (comparator == null) { return ((Comparable<? super K>) k1).compareTo(k2); } return comparator.compare(k1, k2); } public boolean validateRefAndTryLock(Node parent, Node child, boolean left) { boolean ret = false; if (left) { ret = parent.tryWriteLockWithConditionRefLeft(child); } else { ret = parent.tryWriteLockWithConditionRefRight(child); } if (parent.deleted) { if (ret) { if (left) { parent.unlockWriteLeft(); } else { parent.unlockWriteRight(); } } return false; } return ret; } public boolean validateValAndTryLock(Node parent, Node child, boolean left) { boolean ret = false; if (left) { ret = parent.tryWriteLockWithConditionValLeft(child); } else { ret = parent.tryWriteLockWithConditionValRight(child); } if (parent.deleted) { if (ret) { if (left) { parent.unlockWriteLeft(); } else { parent.unlockWriteRight(); } } return false; } return ret; } public void undoValidateAndTryLock(Node parent, boolean left) { if (left) { parent.unlockWriteLeft(); } else { parent.unlockWriteRight(); } } public void undoValidateAndTryLock(Node parent, Node child) { undoValidateAndTryLock(parent, compare(child.key, parent.key) < 0); } public class Window { Node curr, prev, gprev; public Window() { this.prev = ROOT; this.curr = ROOT.l; } public void reset() { this.prev = ROOT; this.curr = ROOT.l; } public void set(Node curr, Node prev) { this.prev = prev; this.curr = curr; } public void add(Node next) { gprev = prev; prev = curr; curr = next; } } public Window traverse(Object key, Window window) { final Comparable<? super K> k = comparable(key); int comparison; Node curr = window.curr; while (curr != null) { comparison = k.compareTo(curr.key); if (comparison == 0) { return window; } if (comparison < 0) { curr = curr.l; } else { curr = curr.r; } window.add(curr); } return window; } @Override public V putIfAbsent(K key, V value) { final Window window = new Window(); while (true) { final Node curr = traverse(key, window).curr; final int comparison = curr == null ? (window.prev.key == null ? -1 : compare(key, window.prev.key)) : (curr.key == null ? -1 : compare(key, curr.key)); if (comparison == 0) { while (true) { if (curr.deleted) { window.reset(); break; } V get = curr.value; if (get != null) { return get; } if (curr.tryWriteLockWithConditionState(false)) { // Already checked on deleted curr.value = value; curr.unlockWriteState(); return null; } } } else { final Node prev = window.prev; final Node node = new Node(key, value); final boolean left = comparison < 0; if (validateRefAndTryLock(prev, null, left)) { prev.readLockState(); if (!prev.deleted) { if (left) { prev.l = node; } else { prev.r = node; } prev.unlockReadState(); undoValidateAndTryLock(prev, left); return null; } prev.unlockReadState(); undoValidateAndTryLock(prev, left); } if (prev.deleted) { window.reset(); } else { window.set(window.prev, window.gprev); } } } } public int numberOfChildren(Node l, Node r) { return (l == null ? 0 : 1) + (r == null ? 0 : 1); } public V remove(final Object key) { V get = null; final Window window = new Window(); while (true) { window.reset(); Node curr = traverse(key, window).curr; if (curr == null || curr.value == null || curr.deleted) { return null; } Node left = curr.l; Node right = curr.r; int nc = numberOfChildren(left, right); if (nc == 2) { if (!curr.tryWriteLockWithConditionState(true)) { continue; } if (curr.numberOfChildren() != 2) { curr.unlockWriteState(); continue; } get = curr.value; curr.value = null; curr.unlockWriteState(); return get; } else if (nc == 1) { final Node child; boolean leftChild = false; if (left != null) { leftChild = true; child = left; } else { child = right; } final Node prev = window.prev; final boolean leftCurr = prev.key == null || compare(curr.key, prev.key) < 0; if (!validateRefAndTryLock(curr, child, leftChild)) { continue; } if (!validateRefAndTryLock(prev, curr, leftCurr)) { undoValidateAndTryLock(curr, leftChild); continue; } if (!curr.tryWriteLockWithConditionState(true)) { undoValidateAndTryLock(prev, leftCurr); undoValidateAndTryLock(curr, leftChild); continue; } if (curr.numberOfChildren() != 1) { curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); undoValidateAndTryLock(curr, leftChild); continue; } get = curr.value; curr.deleted = true; if (leftCurr) { prev.l = child; } else { prev.r = child; } curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); undoValidateAndTryLock(curr, leftChild); return get; } else { final Node prev = window.prev; final boolean leftCurr = prev.key == null || compare(curr.key, prev.key) < 0; if (prev.value != null) { if (!validateValAndTryLock(prev, curr, leftCurr)) { continue; } if (leftCurr) { curr = prev.l; } else { curr = prev.r; } if (!curr.tryWriteLockWithConditionState(true)) { undoValidateAndTryLock(prev, leftCurr); continue; } if (curr.numberOfChildren() != 0) { curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); continue; } if (!prev.tryReadLockWithConditionState(true)) { curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); continue; } get = curr.value; curr.deleted = true; if (leftCurr) { prev.l = null; } else { prev.r = null; } prev.unlockReadState(); curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); return get; } else { final Node child; boolean leftChild = false; if (leftCurr) { child = prev.r; } else { child = prev.l; leftChild = true; } final Node gprev = window.gprev; final boolean leftPrev = gprev.key == null || compare(prev.key, gprev.key) < 0; if (!validateValAndTryLock(prev, curr, leftCurr)) { continue; } if (leftCurr) { curr = prev.l; } else { curr = prev.r; } if (!curr.tryWriteLockWithConditionState(true)) { undoValidateAndTryLock(prev, leftCurr); continue; } if (curr.numberOfChildren() != 0) { curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); continue; } if (!validateRefAndTryLock(prev, child, leftChild)) { curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); continue; } if (!validateRefAndTryLock(gprev, prev, leftPrev)) { undoValidateAndTryLock(prev, leftChild); curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); continue; } if (!prev.tryWriteLockWithConditionState(false)) { undoValidateAndTryLock(gprev, leftPrev); undoValidateAndTryLock(prev, leftChild); curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); continue; } prev.deleted = true; get = curr.value; curr.deleted = true; if (leftPrev) { gprev.l = child; } else { gprev.r = child; } prev.unlockWriteState(); undoValidateAndTryLock(gprev, leftPrev); undoValidateAndTryLock(prev, leftChild); curr.unlockWriteState(); undoValidateAndTryLock(prev, leftCurr); return get; } } } } @Override public V get(final Object key) { Node curr = ROOT.l; final Comparable<? super K> k = comparable(key); int comparison; while (curr != null) { comparison = k.compareTo(curr.key); if (comparison == 0) { return curr.deleted ? null : curr.value; } if (comparison < 0) { curr = curr.l; } else { curr = curr.r; } } return null; } @Override public boolean containsKey(Object key) { return get(key) != null; } @Override public Set<Entry<K, V>> entrySet() { throw new AssertionError("Entry set is not implemented"); } public int size(Node v) { if (v == null) { return 0; } assert !v.deleted; return (v.value != null ? 1 : 0) + size(v.l) + size(v.r); } @Override public int size() { return size(ROOT) - 1; } public int hash(Node v, int power) { if (v == null) { return 0; } return v.key.hashCode() * power + hash(v.l, power * 239) + hash(v.r, power * 533); } public int hash() { return hash(ROOT.l, 1); } public int maxDepth(Node v) { if (v == null) { return 0; } return 1 + Math.max(maxDepth(v.l), maxDepth(v.r)); } public int sumDepth(Node v, int d) { if (v == null) { return 0; } return (v.value == null ? d : 0) + sumDepth(v.l, d + 1) + sumDepth(v.r, d + 1); } public int averageDepth() { return (sumDepth(ROOT, -1) + 1) / size(); } public int maxDepth() { return maxDepth(ROOT) - 1; } public boolean stopMaintenance() { System.out.println("Average depth: " + averageDepth()); System.out.println("Depth: " + maxDepth()); System.out.println("Total depth: " + (sumDepth(ROOT, -1) + 1)); System.out.println("Hash: " + hash()); return true; } public int numNodes(Node v) { return v == null ? 0 : 1 + numNodes(v.l) + numNodes(v.r); } public int numNodes() { return numNodes(ROOT) - 1; } public long getStructMods() { return 0; } @Override public void clear() { ROOT.l = null; ROOT.r = null; } }