/*
* Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* (see java/src/trees/lockbased/catreeutils/LICENSE)
*/
package trees.lockbased.catreeutils;
import java.util.Random;
import java.lang.reflect.Field;
import java.util.*;
import java.io.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.StampedLock;
import java.nio.ByteBuffer;
import sun.misc.Unsafe;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* Objects of this class are used by the CA tree implementation in
* "../CATreeMapAVL.java". Please see that file for details. This class
* implements necessary functionality for a CA tree base node with AVL
* trees as sequential data structure.
*
* @author Kjell Winblad
*/
public class DualLFCASAVLTreeMapSTD<K, V> extends AbstractMap<K,V> implements SplitableAndJoinableMap<K, V>, Invalidatable, AnyKeyProviding<K>{
// ===== Fields =================
private final SeqLock lock = new SeqLock();
private volatile boolean valid = true;
private int size = 0;
//Use setRoot and getRoot to access the root
private volatile STDAVLNodeDCAS<K,V> theRoot = null;
private Object parent = null;
private final Comparator<? super K> comparator;
private static final AtomicReferenceFieldUpdater<DualLFCASAVLTreeMapSTD, STDAVLNodeDCAS> rootUpdater =
AtomicReferenceFieldUpdater.newUpdater(DualLFCASAVLTreeMapSTD.class, STDAVLNodeDCAS.class, "theRoot");
private volatile STDAVLNodeDCAS<K,V> lockFreeHolder = null;
private byte[] localStatisticsArray = null;
// ==============================
// ===== Constants ==============
private static final Unsafe unsafe;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
} catch (Exception ex) {
throw new Error(ex);
}
}
private final static boolean DEBUG = false;
private static final int STAT_LOCK_HIGH_CONTENTION_LIMIT = 1000;
private static final int STAT_LOCK_LOW_CONTENTION_LIMIT = -1000;
private static final int STAT_LOCK_FAILURE_CONTRIB = 250;
private static final int STAT_LOCK_SUCCESS_CONTRIB = 1;
private static final boolean LOCK_FREE_FALSE_SHARING_SAFETY = false;
private static final int NUMBER_OF_LOCAL_STATISTICS_SLOTS = Runtime.getRuntime().availableProcessors();
private static final long LOCAL_STATISTICS_ARRAY_WRITE_INDICATOR_OFFSET = 0;
private static final long LOCAL_STATISTICS_ARRAY_SIZE_OFFSET = 8;
private static final long LOCAL_STATISTICS_ARRAY_START_OFFSET =
128 + unsafe.ARRAY_BYTE_BASE_OFFSET;
private static final long LOCAL_STATISTICS_ARRAY_SLOT_SIZE = 192;
// ==============================
// ==== Debugging and Testing ====
final private int avlValidateP(STDAVLNodeDCAS<K,V> toTest){
if(toTest != null && toTest.parent != null){
System.out.println("Parent should be null\n");
printDot(getRoot(), "parent_should_be_null");
throw new RuntimeException();
}
return avlValidate(toTest);
}
final private int avlValidate(STDAVLNodeDCAS<K,V> toTest){
if(toTest == null){
return 0;
}else{
int hl = avlValidate(toTest.getLeft());
if(toTest.getLeft() != null && toTest.getLeft().parent != toTest){
System.out.println("WRONG PARENT\n");
printDot(getRoot(), "wrong_parent");
throw new RuntimeException();
}
int hr = avlValidate(toTest.getRight());
if(toTest.getRight() != null && toTest.getRight().parent != toTest){
System.out.println("WRONG PARENT\n");
printDot(getRoot(), "wrong_parent");
throw new RuntimeException();
}
if(toTest.balance == 0 && hl != hr){
System.out.println("FAIL 1 "+hl+" " +hr+"\n");
printDot(getRoot(), "fail1");
throw new RuntimeException();
}else if(toTest.balance == -1 && (hr - hl) != -1){
System.out.println("FAIL 2\n");
printDot(getRoot(), "fail2");
throw new RuntimeException();
}else if(toTest.balance == 1 && (hr - hl) != 1){
System.out.println("FAIL 3 "+(hr - hl)+"\n");
printDot(getRoot(), "fail3");
throw new RuntimeException();
}else if(toTest.balance > 1 || toTest.balance < -1){
System.out.println("FAIL 4\n");
printDot(getRoot(), "fail4");
throw new RuntimeException();
}
if(hl > hr){
return hl + 1;
}else{
return hr + 1;
}
}
}
void printDotHelper(STDAVLNodeDCAS<K,V> node, PrintStream writeTo){
Random rand = new Random();
try{
if(node!=null){
if(node.getLeft() !=null){
writeTo.print("\"" + node.getValue() + ", " + node.balance + ", " + (node.parent != null ? node.parent.key : null) + " \"");
writeTo.print(" -> ");
writeTo.print("\"" + node.getLeft().getValue() + ", " + node.getLeft().balance + ", " + (node.getLeft().parent != null ? node.getLeft().parent.key : null) + " \"");
writeTo.println(";");
}else{
writeTo.print("\"" + node.getValue() + ", " + node.balance + ", " + (node.parent != null ? node.parent.key : null) + " \"");
writeTo.print(" -> ");
writeTo.print("\"" + null+ ", " + rand.nextInt() + " \"");
writeTo.println(";");
}
if(node.getRight() !=null){
writeTo.print("\"" + node.getValue() + ", " + node.balance + ", " + (node.parent != null ? node.parent.key : null) + " \"");
writeTo.print(" -> ");
writeTo.print("\"" + node.getRight().getValue() + ", " + node.getRight().balance + ", " + (node.getRight().parent != null ? node.getRight().parent.key : null) + " \"");
writeTo.println(";");
}else{
writeTo.print("\"" + node.getValue() + ", " + node.balance + ", " + (node.parent != null ? node.parent.key : null) + " \"");
writeTo.print(" -> ");
writeTo.print("\"" + null+ ", " + rand.nextInt() + " \"");
writeTo.println(";");
}
printDotHelper(node.getLeft(), writeTo);
printDotHelper(node.getRight(), writeTo);
}
}catch(Exception e){
e.printStackTrace();
}
}
void printDot(STDAVLNodeDCAS<K,V> node, String fileName){
try{
Process p = new ProcessBuilder("dot", "-Tsvg")
.redirectOutput(ProcessBuilder.Redirect.to(new File(fileName + ".svg")))
.start();
PrintStream writeTo = new PrintStream(p.getOutputStream());
writeTo.print("digraph G{\n");
writeTo.print(" graph [ordering=\"out\"];\n");
printDotHelper(node, writeTo);
writeTo.print("}\n");
writeTo.close();
p.waitFor();
}catch(Exception e){
e.printStackTrace();
}
}
// ============================
// === Constructors ===========
public DualLFCASAVLTreeMapSTD() {
comparator = null;
}
public DualLFCASAVLTreeMapSTD(Comparator<? super K> comparator) {
this.comparator = comparator;
}
// ============================
// === Helper Methods =========
private boolean lfMapForUs(STDAVLNodeDCAS<K,V> lfMap, K key){
if(comparator != null){
return comparator.compare(key, lfMap.getKey()) == 0;
}else{
@SuppressWarnings("unchecked")
Comparable<? super K> keyComp = (Comparable<? super K>) key;
return keyComp.compareTo((K)lfMap.getKey() ) == 0;
}
}
private final int computeHeight(){
STDAVLNodeDCAS<K,V> r = getRoot();
if(r == null){
return 0;
} else {
STDAVLNodeDCAS<K,V> currentNode = r;
int hightSoFar = 1;
while(currentNode.getLeft() != null || currentNode.getRight() != null){
if(currentNode.balance == -1){
currentNode = currentNode.getLeft();
}else{
currentNode = currentNode.getRight();
}
hightSoFar = hightSoFar + 1;
}
return hightSoFar;
}
}
private final K minKey(){
STDAVLNodeDCAS<K,V> currentNode = getRoot();
if(currentNode == null){
return null;
}
while(currentNode.getLeft() != null){
currentNode = currentNode.getLeft();
}
return currentNode.key;
}
private final K maxKey(){
STDAVLNodeDCAS<K,V> currentNode = getRoot();
if(currentNode == null){
return null;
}
while(currentNode.getRight() != null){
currentNode = currentNode.getRight();
}
return currentNode.key;
}
private int countNodes(STDAVLNodeDCAS n){
if(n == null) return 0;
else return 1 + countNodes(n.getLeft()) + countNodes(n.getRight());
}
final private STDAVLNodeDCAS<K,V> getSTDAVLNodeDCASUsingComparator(Object keyParam) {
@SuppressWarnings("unchecked")
K key = (K) keyParam;
STDAVLNodeDCAS<K,V> currentNode = getRoot();
Comparator<? super K> cpr = comparator;
while(currentNode != null){
K nodeKey = currentNode.key;
int compareValue = cpr.compare(key,nodeKey);
if(compareValue < 0) {
currentNode = currentNode.getLeft();
} else if (compareValue > 0) {
currentNode = currentNode.getRight();
} else {
return currentNode;
}
}
return null;
}
final private STDAVLNodeDCAS<K,V> getSTDAVLNodeDCAS(Object keyParam){
if(comparator != null){
return getSTDAVLNodeDCASUsingComparator(keyParam);
}else{
@SuppressWarnings("unchecked")
Comparable<? super K> key = (Comparable<? super K>) keyParam;
STDAVLNodeDCAS<K,V> currentNode = getRoot();
while(currentNode != null){
K nodeKey = currentNode.key;
int compareValue = key.compareTo(nodeKey);
if(compareValue < 0) {
currentNode = currentNode.getLeft();
} else if (compareValue > 0) {
currentNode = currentNode.getRight();
} else {
return currentNode;
}
}
return null;
}
}
private void resetLocalStatisticsArray(){
for(int i = 0; i < NUMBER_OF_LOCAL_STATISTICS_SLOTS; i++){
long sizeOffset =
LOCAL_STATISTICS_ARRAY_START_OFFSET +
i * LOCAL_STATISTICS_ARRAY_SLOT_SIZE +
LOCAL_STATISTICS_ARRAY_SIZE_OFFSET;
unsafe.putLong(localStatisticsArray, sizeOffset, 0);
}
}
final private void rotateLeft(STDAVLNodeDCAS<K,V> prevNode){
STDAVLNodeDCAS<K,V> leftChild = prevNode.getLeft();
STDAVLNodeDCAS<K,V> prevNodeParent = prevNode.parent;
prevNode.setLeft(leftChild.getRight());
if(prevNode.getLeft() != null){
prevNode.getLeft().parent = prevNode;
}
leftChild.setRight(prevNode);
prevNode.parent = leftChild;
prevNode.balance = 0;
if(prevNodeParent == null){
setRoot(leftChild);
}else if(prevNodeParent.getLeft() == prevNode){
prevNodeParent.setLeft(leftChild);
}else{
prevNodeParent.setRight(leftChild);
}
leftChild.parent = prevNodeParent;
leftChild.balance = 0;
}
final private void rotateRight(STDAVLNodeDCAS<K,V> prevNode){
STDAVLNodeDCAS<K,V> rightChild = prevNode.getRight();
STDAVLNodeDCAS<K,V> prevNodeParent = prevNode.parent;
prevNode.setRight(rightChild.getLeft());
if(prevNode.getRight() != null){
prevNode.getRight().parent = prevNode;
}
rightChild.setLeft(prevNode);
prevNode.parent = rightChild;
prevNode.balance = 0;
if(prevNodeParent == null){
setRoot(rightChild);
}else if(prevNodeParent.getLeft() == prevNode){
prevNodeParent.setLeft(rightChild);
}else{
prevNodeParent.setRight(rightChild);
}
rightChild.parent = prevNodeParent;
rightChild.balance = 0;
}
final private void rotateDoubleRight(STDAVLNodeDCAS<K,V> prevNode){
STDAVLNodeDCAS<K,V> prevNodeParent = prevNode.parent;
STDAVLNodeDCAS<K,V> leftChild = prevNode.getLeft();
STDAVLNodeDCAS<K,V> leftChildRightChild = leftChild.getRight();
leftChild.setRight(leftChildRightChild.getLeft());
if(leftChildRightChild.getLeft() != null){
leftChildRightChild.getLeft().parent = leftChild;
}
leftChildRightChild.setLeft(leftChild);
leftChild.parent = leftChildRightChild;
prevNode.setLeft(leftChildRightChild.getRight());
if(leftChildRightChild.getRight() != null){
leftChildRightChild.getRight().parent = prevNode;
}
leftChildRightChild.setRight(prevNode);
prevNode.parent = leftChildRightChild;
prevNode.balance = (leftChildRightChild.balance == -1) ? +1 : 0;
leftChild.balance = (leftChildRightChild.balance == 1) ? -1 : 0;
if(prevNodeParent == null){
setRoot(leftChildRightChild);
}else if(prevNodeParent.getLeft() == prevNode){
prevNodeParent.setLeft(leftChildRightChild);
}else{
prevNodeParent.setRight(leftChildRightChild);
}
leftChildRightChild.parent = prevNodeParent;
leftChildRightChild.balance = 0;
}
final private void rotateDoubleLeft(STDAVLNodeDCAS<K,V> prevNode){
STDAVLNodeDCAS<K,V> prevNodeParent = prevNode.parent;
STDAVLNodeDCAS<K,V> rightChild = prevNode.getRight();
STDAVLNodeDCAS<K,V> rightChildLeftChild = rightChild.getLeft();
rightChild.setLeft(rightChildLeftChild.getRight());
if(rightChildLeftChild.getRight() != null){
rightChildLeftChild.getRight().parent = rightChild;
}
rightChildLeftChild.setRight(rightChild);
rightChild.parent = rightChildLeftChild;
prevNode.setRight(rightChildLeftChild.getLeft());
if(rightChildLeftChild.getLeft() != null){
rightChildLeftChild.getLeft().parent = prevNode;
}
rightChildLeftChild.setLeft(prevNode);
prevNode.parent = rightChildLeftChild;
prevNode.balance = (rightChildLeftChild.balance == 1) ? -1 : 0;
rightChild.balance = (rightChildLeftChild.balance == -1) ? 1 : 0;
if(prevNodeParent == null){
setRoot(rightChildLeftChild);
}else if(prevNodeParent.getLeft() == prevNode){
prevNodeParent.setLeft(rightChildLeftChild);
}else{
prevNodeParent.setRight(rightChildLeftChild);
}
rightChildLeftChild.parent = prevNodeParent;
rightChildLeftChild.balance = 0;
}
private V put(K keyParam, V value, boolean replace){
if(DEBUG) avlValidateP(getRoot());
STDAVLNodeDCAS<K,V> prevNode = null;
STDAVLNodeDCAS<K,V> currentNode = getRoot();
boolean dirLeft = true;
if(comparator != null){
K key = keyParam;
Comparator<? super K> cpr = comparator;
while(currentNode != null){
K nodeKey = currentNode.key;
int compareValue = cpr.compare(key, nodeKey);
if(compareValue < 0) {
dirLeft = true;
prevNode = currentNode;
currentNode = currentNode.getLeft();
} else if (compareValue > 0) {
dirLeft = false;
prevNode = currentNode;
currentNode = currentNode.getRight();
} else {
V prevValue = currentNode.getValue();
if(replace){
currentNode.setValue(value);
}
if(DEBUG) avlValidateP(getRoot());
return prevValue;
}
}
}else{
@SuppressWarnings("unchecked")
Comparable<? super K> key = (Comparable<? super K>) keyParam;
while(currentNode != null){
K nodeKey = currentNode.key;
int compareValue = key.compareTo(nodeKey);
if(compareValue < 0) {
dirLeft = true;
prevNode = currentNode;
currentNode = currentNode.getLeft();
} else if (compareValue > 0) {
dirLeft = false;
prevNode = currentNode;
currentNode = currentNode.getRight();
} else {
V prevValue = currentNode.getValue();
currentNode.setValue(value);
if(DEBUG) avlValidateP(getRoot());
return prevValue;
}
}
}
//Insert node
size = size + 1;
currentNode = new STDAVLNodeDCAS<K,V>(keyParam, value);
if(prevNode == null){
setRoot(currentNode);
}else if(dirLeft){
prevNode.setLeft(currentNode);
}else{
prevNode.setRight(currentNode);
}
currentNode.parent = prevNode;
// Balance
while(prevNode != null){
if(prevNode.getLeft() == currentNode){
if(prevNode.balance == -1){
STDAVLNodeDCAS<K,V> leftChild = prevNode.getLeft();
// Need to rotate
if(leftChild.balance == -1){
rotateLeft(prevNode);
}else{
rotateDoubleRight(prevNode);
}
if(DEBUG) avlValidateP(getRoot());
return null; // Parents not affected balance restored
}else if(prevNode.balance == 0){
prevNode.balance = -1;
}else{
prevNode.balance = 0;
break; // balanced
}
}else{
if(prevNode.balance == 1){
STDAVLNodeDCAS<K,V> rightChild = prevNode.getRight();
// Need to rotate
if(rightChild.balance == 1){
rotateRight(prevNode);
}else{
rotateDoubleLeft(prevNode);
}
if(DEBUG) avlValidateP(getRoot());
return null; // Parents not affected balance restored
}else if (prevNode.balance == 0){
prevNode.balance = 1;
}else{
prevNode.balance = 0;
break; // balanced
}
}
currentNode = prevNode;
prevNode = prevNode.parent;
}
if(DEBUG) avlValidateP(getRoot());
return null;
}
final private boolean replaceWithRightmost(STDAVLNodeDCAS<K,V> toReplaceInNode){
STDAVLNodeDCAS<K,V> currentNode = toReplaceInNode.getLeft();
int replacePos = 0;
while (currentNode.getRight() != null) {
replacePos = replacePos + 1;
currentNode = currentNode.getRight();
}
toReplaceInNode.key = currentNode.key;
toReplaceInNode.setValue(currentNode.getValue());
if(currentNode.parent.getRight() == currentNode){
currentNode.parent.setRight(currentNode.getLeft());
}else{
currentNode.parent.setLeft(currentNode.getLeft());
}
if(currentNode.getLeft() != null){
currentNode.getLeft().parent = currentNode.parent;
}
boolean continueBalance = true;
currentNode = currentNode.parent;
while (replacePos > 0 && continueBalance) {
STDAVLNodeDCAS<K,V> operateOn = currentNode;
currentNode = currentNode.parent;
replacePos = replacePos - 1;
continueBalance = deleteBalanceRight(operateOn);
}
return continueBalance;
}
final private boolean deleteBalanceLeft(STDAVLNodeDCAS<K,V> currentNode){
boolean continueBalance = true;
if(currentNode.balance == -1){
currentNode.balance = 0;
}else if(currentNode.balance == 0){
currentNode.balance = 1;
continueBalance = false;
}else{
STDAVLNodeDCAS<K,V> currentNodeParent = currentNode.parent;
STDAVLNodeDCAS<K,V> rightChild = currentNode.getRight();
int rightChildBalance = rightChild.balance;
if (rightChildBalance >= 0) { //Single RR rotation
rotateRight(currentNode);
if(rightChildBalance == 0){
currentNode.balance = 1;
rightChild.balance = -1;
continueBalance = false;
}
} else { //Double LR rotation
STDAVLNodeDCAS<K,V> rightChildLeftChild = rightChild.getLeft();
int rightChildLeftChildBalance = rightChildLeftChild.balance;
rightChild.setLeft(rightChildLeftChild.getRight());
if(rightChildLeftChild.getRight() != null){
rightChildLeftChild.getRight().parent = rightChild;
}
rightChildLeftChild.setRight(rightChild);
rightChild.parent = rightChildLeftChild;
currentNode.setRight(rightChildLeftChild.getLeft());
if(rightChildLeftChild.getLeft() != null){
rightChildLeftChild.getLeft().parent = currentNode;
}
rightChildLeftChild.setLeft(currentNode);
currentNode.parent = rightChildLeftChild;
currentNode.balance = (rightChildLeftChildBalance == 1) ? -1 : 0;
rightChild.balance = (rightChildLeftChildBalance == -1) ? 1 : 0;
rightChildLeftChild.balance = 0;
if(currentNodeParent == null){
setRoot(rightChildLeftChild);
}else if(currentNodeParent.getLeft() == currentNode){
currentNodeParent.setLeft(rightChildLeftChild);
}else{
currentNodeParent.setRight(rightChildLeftChild);
}
rightChildLeftChild.parent = currentNodeParent;
}
}
return continueBalance;
}
final private boolean deleteBalanceRight(STDAVLNodeDCAS<K,V> currentNode){
boolean continueBalance = true;
if(currentNode.balance == 1){
currentNode.balance = 0;
}else if(currentNode.balance == 0){
currentNode.balance = -1;
continueBalance = false;
}else{
STDAVLNodeDCAS<K,V> currentNodeParent = currentNode.parent;
STDAVLNodeDCAS<K,V> leftChild = currentNode.getLeft();
int leftChildBalance = leftChild.balance;
if (leftChildBalance <= 0) { //Single LL rotation
rotateLeft(currentNode);
if(leftChildBalance == 0){
currentNode.balance = -1;
leftChild.balance = 1;
continueBalance = false;
}
} else { //Double LR rotation
STDAVLNodeDCAS<K,V> leftChildRightChild = leftChild.getRight();
int leftChildRightChildBalance = leftChildRightChild.balance;
leftChild.setRight(leftChildRightChild.getLeft());
if(leftChildRightChild.getLeft() != null){
leftChildRightChild.getLeft().parent = leftChild;//null pointer exeception
}
leftChildRightChild.setLeft(leftChild);
leftChild.parent = leftChildRightChild;
currentNode.setLeft(leftChildRightChild.getRight());
if(leftChildRightChild.getRight() != null){
leftChildRightChild.getRight().parent = currentNode;//null pointer exception
}
leftChildRightChild.setRight(currentNode);
currentNode.parent = leftChildRightChild;
currentNode.balance = (leftChildRightChildBalance == -1) ? 1 : 0;
leftChild.balance = (leftChildRightChildBalance == 1) ? -1 : 0;
leftChildRightChild.balance = 0;
if(currentNodeParent == null){
setRoot(leftChildRightChild);
}else if(currentNodeParent.getLeft() == currentNode){
currentNodeParent.setLeft(leftChildRightChild);
}else{
currentNodeParent.setRight(leftChildRightChild);
}
leftChildRightChild.parent = currentNodeParent;
}
}
return continueBalance;
}
final private void addAllToList(final STDAVLNodeDCAS<K,V> node, ArrayList<Map.Entry<K, V>> list){
if(node!=null){
addAllToList(node.getLeft(), list);
AbstractMap.SimpleImmutableEntry<K,V> entry = new AbstractMap.SimpleImmutableEntry<K,V>(node.key, node.getValue()){
public int hashCode(){
return node.key.hashCode();
}
};
list.add(entry);
addAllToList(node.getRight(), list);
}
}
final public void addAllToList(ArrayList<Map.Entry<K, V>> list){
addAllToList(getRoot(), list);
}
// ============================
// === Public Interface =======
public void setParent(Object parent){
this.parent = parent;
}
public Object getParent(){
return parent;
}
public STDAVLNodeDCAS<K,V> getRoot(){
return theRoot;
}
public void setRoot(STDAVLNodeDCAS<K,V> n){
rootUpdater.lazySet(this, n);
}
public String toString(){
return "B(" + getRoot() + ", " + isValid() + "," + getStatistics() + "," + getParent() + ","+size()+")";
}
public K anyKey(){
if(getRoot() != null){
return getRoot().key;
}else{
return null;
}
}
public boolean isValid(){
return valid;
}
public void invalidate(){
valid = false;
}
public boolean tryLock(){
if(lock.tryLock()){
if(isLockFreeMode()){
transformToLocked();
}
return true;
}else{
return false;
}
}
public void lock(){
if (tryLock()) {
lock.subFromContentionStatistics();
return;
}
lock.lock();
if(isLockFreeMode()){
transformToLocked();
}
lock.addToContentionStatistics();
}
public boolean lockIfNotLockFreeWithKey(K key){
STDAVLNodeDCAS<K,V> lfMap = getLockFreeMap();
if(lfMap != null && lfMapForUs(lfMap, key)){
while(lfMap != null && lfMapForUs(lfMap, key) && getOptimisticReadToken() == 0){
Thread.yield();
lfMap = getLockFreeMap();
}
if(lfMap != null && lfMapForUs(lfMap, key)){
return false;
}
}
if (lock.tryLock()) {
lfMap = getLockFreeMap();
if(lfMap != null){
if(lfMapForUs(lfMap, key)){
lock.unlock();
return false;
}else{
transformToLocked();
}
}
lock.subFromContentionStatistics();
return true;
}
lock.lock();
lfMap = getLockFreeMap();
if(lfMap != null){
if(lfMapForUs(lfMap, key)){
lock.unlock();
return false;
}else{
transformToLocked();
}
}
lock.addToContentionStatistics();
return true;
}
public boolean lockIfNotLockFree(){
STDAVLNodeDCAS<K,V> lfMap = getLockFreeMap();
if(lfMap != null){
while(lfMap != null && getOptimisticReadToken() == 0){
Thread.yield();
lfMap = getLockFreeMap();
}
if(lfMap != null){
return false;
}
}
if (lock.tryLock()) {
lfMap = getLockFreeMap();
if(lfMap != null){
lock.unlock();
return false;
}
lock.subFromContentionStatistics();
return true;
}
lock.lock();
lfMap = getLockFreeMap();
if(lfMap != null){
lock.unlock();
return false;
}
lock.addToContentionStatistics();
return true;
}
public void addToContentionStatistics(){
lock.addToContentionStatistics();
}
public void unlock(){
lock.unlock();
}
public long getOptimisticReadToken(){
return lock.tryOptimisticRead();
}
public boolean validateOptimisticReadToken(long optimisticReadToken){
return lock.validate(optimisticReadToken);
}
public int getStatistics(){
return lock.getLockStatistics();
}
public void resetStatistics(){
lock.resetStatistics();
}
public int getHighContentionLimit(){
return STAT_LOCK_HIGH_CONTENTION_LIMIT;
}
public int getLowContentionLimit(){
return STAT_LOCK_LOW_CONTENTION_LIMIT;
}
public boolean isHighContentionLimitReached(){
return lock.isHighContentionLimitReached();
}
public boolean isLowContentionLimitReached(){
return lock.isLowContentionLimitReached();
}
public void indicateWriteStart(){
long slot = Thread.currentThread().getId() % NUMBER_OF_LOCAL_STATISTICS_SLOTS;
long writeIndicatorOffset =
LOCAL_STATISTICS_ARRAY_START_OFFSET +
slot * LOCAL_STATISTICS_ARRAY_SLOT_SIZE +
LOCAL_STATISTICS_ARRAY_WRITE_INDICATOR_OFFSET;
long prevValue;
prevValue = unsafe.getLongVolatile(localStatisticsArray,
writeIndicatorOffset);
while(!unsafe.compareAndSwapLong(localStatisticsArray,
writeIndicatorOffset,
prevValue,
prevValue + 1)){
unsafe.fullFence();
unsafe.fullFence();
prevValue = unsafe.getLongVolatile(localStatisticsArray,
writeIndicatorOffset);
}
}
public void indicateWriteEnd(){
long slot = Thread.currentThread().getId() % NUMBER_OF_LOCAL_STATISTICS_SLOTS;
long writeIndicatorOffset =
LOCAL_STATISTICS_ARRAY_START_OFFSET +
slot * LOCAL_STATISTICS_ARRAY_SLOT_SIZE +
LOCAL_STATISTICS_ARRAY_WRITE_INDICATOR_OFFSET;
long prevValue;
prevValue = unsafe.getLongVolatile(localStatisticsArray,
writeIndicatorOffset);
while(!unsafe.compareAndSwapLong(localStatisticsArray,
writeIndicatorOffset,
prevValue,
prevValue - 1)){
unsafe.fullFence();
unsafe.fullFence();
prevValue = unsafe.getLongVolatile(localStatisticsArray,
writeIndicatorOffset);
}
}
public long increaseLocalSize(){
long slot = Thread.currentThread().getId() % NUMBER_OF_LOCAL_STATISTICS_SLOTS;
long sizeOffset =
LOCAL_STATISTICS_ARRAY_START_OFFSET +
slot * LOCAL_STATISTICS_ARRAY_SLOT_SIZE +
LOCAL_STATISTICS_ARRAY_SIZE_OFFSET;
long prevValue;
do{
prevValue = unsafe.getLongVolatile(localStatisticsArray,
sizeOffset);
}while(!unsafe.compareAndSwapLong(localStatisticsArray,
sizeOffset,
prevValue,
prevValue + 1));
return prevValue + 1;
}
public long decreaseLocalSize(){
long slot = Thread.currentThread().getId() % NUMBER_OF_LOCAL_STATISTICS_SLOTS;
long sizeOffset =
LOCAL_STATISTICS_ARRAY_START_OFFSET +
slot * LOCAL_STATISTICS_ARRAY_SLOT_SIZE +
LOCAL_STATISTICS_ARRAY_SIZE_OFFSET;
long prevValue;
do{
prevValue = unsafe.getLongVolatile(localStatisticsArray,
sizeOffset);
}while(!unsafe.compareAndSwapLong(localStatisticsArray,
sizeOffset,
prevValue,
prevValue - 1));
return prevValue -1;
}
public long readLocalSizeSum(){
long sum = 0;
for(int i = 0; i < NUMBER_OF_LOCAL_STATISTICS_SLOTS; i++){
long sizeOffset =
LOCAL_STATISTICS_ARRAY_START_OFFSET +
i * LOCAL_STATISTICS_ARRAY_SLOT_SIZE +
LOCAL_STATISTICS_ARRAY_SIZE_OFFSET;
sum = sum + unsafe.getLongVolatile(localStatisticsArray, sizeOffset);
}
return sum;
}
public void waitNoOngoingWrite(){
long currentOffset =
LOCAL_STATISTICS_ARRAY_START_OFFSET +
LOCAL_STATISTICS_ARRAY_WRITE_INDICATOR_OFFSET;
for(int i = 0; i < NUMBER_OF_LOCAL_STATISTICS_SLOTS; i++){
while(0 != unsafe.getLongVolatile(localStatisticsArray,
currentOffset)){
unsafe.fullFence();
unsafe.fullFence();
}
currentOffset = currentOffset + LOCAL_STATISTICS_ARRAY_SLOT_SIZE;
}
}
public SplitableAndJoinableMap<K, V> join(SplitableAndJoinableMap<K, V> right){
STDAVLNodeDCAS<K,V> prevNode = null;//f
STDAVLNodeDCAS<K,V> currentNode = null;//f
DualLFCASAVLTreeMapSTD<K, V> newTree = null;
if(comparator == null){
newTree = new DualLFCASAVLTreeMapSTD<K, V>();
}else{
newTree = new DualLFCASAVLTreeMapSTD<K, V>(comparator);
}
DualLFCASAVLTreeMapSTD<K,V> leftTree = this;
DualLFCASAVLTreeMapSTD<K,V> rightTree = (DualLFCASAVLTreeMapSTD<K,V>)right;
if(leftTree.getRoot() == null){
newTree.setRoot(rightTree.getRoot());
newTree.size = rightTree.size + leftTree.size;
return newTree;
}else if(rightTree.getRoot() == null){
newTree.setRoot(leftTree.getRoot());
newTree.size = leftTree.size + rightTree.size;
return newTree;
}
int leftHeight = leftTree.computeHeight();
int rightHeight = rightTree.computeHeight();
if(leftHeight >= rightHeight){
K minKey = rightTree.minKey();
V minValue = rightTree.remove(minKey);
rightTree.size = rightTree.size + 1;
STDAVLNodeDCAS<K,V> newRoot = new STDAVLNodeDCAS<K,V>(minKey, minValue);
int newRightHeight = rightTree.computeHeight();
// Find a node v on the rightmost path from the root of T1 , whose height is either h or h + 1, as follows:
// From: http://www.cs.toronto.edu/~avner/teaching/263/A/2sol.pdf
// v <- root(T1 )
// h' <- h1
// while h > h + 1 do
// if balance factor (v) = -1
// then h' <- h' - 2
// else h' <- h- - 1
// v <- rightchild(v)
prevNode = null;
currentNode = leftTree.getRoot();
int currentHeight = leftHeight;
while(currentHeight > newRightHeight + 1){
if(currentNode.balance == -1){
currentHeight = currentHeight - 2;
}else{
currentHeight = currentHeight - 1;
}
prevNode = currentNode;
currentNode = currentNode.getRight();
}
STDAVLNodeDCAS<K,V> oldCurrentNodeParent = prevNode;
newRoot.setLeft(currentNode);
if(currentNode != null){
currentNode.parent = newRoot;
}
newRoot.setRight(rightTree.getRoot());
if(rightTree.getRoot() != null){
rightTree.getRoot().parent = newRoot;
}
newRoot.balance = newRightHeight - currentHeight;
if(oldCurrentNodeParent == null){//Check if this can happen at all
newTree.setRoot(newRoot);
}else if(oldCurrentNodeParent.getLeft() == currentNode){
oldCurrentNodeParent.setLeft(newRoot);
newRoot.parent = oldCurrentNodeParent;
newTree.setRoot(leftTree.getRoot());
}else{
oldCurrentNodeParent.setRight(newRoot);
newRoot.parent = oldCurrentNodeParent;
newTree.setRoot(leftTree.getRoot());
}
currentNode = newRoot;
}else{
//This case is symetric to the previous case
K maxKey = leftTree.maxKey();//f
V maxValue = leftTree.remove(maxKey);//f
leftTree.size = leftTree.size + 1;
STDAVLNodeDCAS<K,V> newRoot = new STDAVLNodeDCAS<K,V>(maxKey, maxValue);//f
int newLeftHeight = leftTree.computeHeight();//f
prevNode = null;//f
currentNode = rightTree.getRoot();//f
int currentHeight = rightHeight;//f
while(currentHeight > newLeftHeight + 1){//f
if(currentNode.balance == 1){//f
currentHeight = currentHeight - 2;//f
}else{
currentHeight = currentHeight - 1;//f
}
prevNode = currentNode;//f
currentNode = currentNode.getLeft();//f
}
STDAVLNodeDCAS<K,V> oldCurrentNodeParent = prevNode;//f
newRoot.setRight(currentNode);//f
if(currentNode != null){
currentNode.parent = newRoot;//f
}
newRoot.setLeft(leftTree.getRoot());//f
if(leftTree.getRoot() != null){
leftTree.getRoot().parent = newRoot;//f
}
newRoot.balance = currentHeight - newLeftHeight;//f
if(oldCurrentNodeParent == null){//Check if this can happen at all
newTree.setRoot(newRoot);
}else if(oldCurrentNodeParent.getLeft() == currentNode){
oldCurrentNodeParent.setLeft(newRoot);
newRoot.parent = oldCurrentNodeParent;
newTree.setRoot(rightTree.getRoot());
}else{
oldCurrentNodeParent.setRight(newRoot);
newRoot.parent = oldCurrentNodeParent;
newTree.setRoot(rightTree.getRoot());
}
currentNode = newRoot;
}
//Now we need to continue as if this was during the insert
while(prevNode != null){
if(prevNode.getLeft() == currentNode){
if(prevNode.balance == -1){
STDAVLNodeDCAS<K,V> leftChild = prevNode.getLeft();
//Need to rotate
if(leftChild.balance == -1){
newTree.rotateLeft(prevNode);
}else{
newTree.rotateDoubleRight(prevNode);
}
newTree.size = leftTree.size + rightTree.size;
if(DEBUG) avlValidateP(newTree.getRoot());
return newTree; //Parents not affected balance restored
}else if(prevNode.balance == 0){
prevNode.balance = -1;
}else{
prevNode.balance = 0;
break;//balanced
}
}else{
if(prevNode.balance == 1){
STDAVLNodeDCAS<K,V> rightChild = prevNode.getRight();
//Need to rotate
if(rightChild.balance == 1){
newTree.rotateRight(prevNode);
}else{
newTree.rotateDoubleLeft(prevNode);
}
newTree.size = leftTree.size + rightTree.size;
if(DEBUG) avlValidateP(newTree.getRoot());
return newTree;
}else if (prevNode.balance == 0){
prevNode.balance = 1;
}else{
prevNode.balance = 0;
break;//Balanced
}
}
currentNode = prevNode;
prevNode = prevNode.parent;
}
newTree.size = leftTree.size + rightTree.size;
if(DEBUG) avlValidateP(newTree.getRoot());
return newTree;
}
public SplitableAndJoinableMap<K, V> split(Object[] splitKeyWriteBack,
SplitableAndJoinableMap<K, V>[] rightTreeWriteBack){
STDAVLNodeDCAS<K,V> leftRoot = null;
STDAVLNodeDCAS<K,V> rightRoot = null;
if(getRoot() == null){
return null;
}else if(getRoot().getLeft() == null && getRoot().getRight() == null){
return null;
}else if(getRoot().getLeft() == null){
splitKeyWriteBack[0] = getRoot().getRight().key;
rightRoot = getRoot().getRight();
rightRoot.parent = null;
rightRoot.balance = 0;
getRoot().setRight(null);
leftRoot = getRoot();
leftRoot.balance = 0;
}else{
splitKeyWriteBack[0] = getRoot().key;
leftRoot = getRoot().getLeft();
leftRoot.parent = null;
getRoot().setLeft(null);
if (getRoot().getRight() == null){
rightRoot = getRoot();
rightRoot.balance = 0;
}else{
K insertKey = getRoot().key;
V insertValue = getRoot().getValue();
setRoot(getRoot().getRight());
getRoot().parent = null;
put(insertKey, insertValue);
size = size - 1;
rightRoot = getRoot();
}
}
DualLFCASAVLTreeMapSTD<K,V> leftTree = null;
if(comparator == null){
leftTree = new DualLFCASAVLTreeMapSTD<K, V>();
}else{
leftTree = new DualLFCASAVLTreeMapSTD<K, V>(comparator);
}
leftTree.setRoot(leftRoot);
DualLFCASAVLTreeMapSTD<K,V> rightTree = null;
if(comparator == null){
rightTree = new DualLFCASAVLTreeMapSTD<K, V>();
}else{
rightTree = new DualLFCASAVLTreeMapSTD<K, V>(comparator);
}
rightTree.setRoot(rightRoot);
int remainder = size % 2;
int aproxSizes = size / 2;
leftTree.size = aproxSizes;
rightTree.size = aproxSizes + remainder;
rightTreeWriteBack[0] = rightTree;
if(DEBUG) {
avlValidateP(leftTree.getRoot());
avlValidateP(rightTree.getRoot());
}
return leftTree;
}
public void transformToLockFree(){
if(LOCK_FREE_FALSE_SHARING_SAFETY){
lockFreeHolder = new STDAVLNodeDCASNOFALSE<K,V>(getRoot().getKey(), getRoot().getValue());
}else{
lockFreeHolder = getRoot();
}
setRoot(null);
if(localStatisticsArray == null){
localStatisticsArray =
new byte[128*2 + (int)LOCAL_STATISTICS_ARRAY_SLOT_SIZE*NUMBER_OF_LOCAL_STATISTICS_SLOTS];
}
}
public void transformToLocked(){
STDAVLNodeDCAS<K,V> lfMap = lockFreeHolder;
lockFreeHolder = null;
waitNoOngoingWrite();
if(lfMap.getValue() == null){
size = size - 1;
setRoot(null);
}else{
setRoot(lfMap);
}
resetStatistics(); // Reset statistics so it will not be converted too quickly
}
public boolean isLockFreeMode(){
return lockFreeHolder != null;
}
public boolean oneElement(){
return theRoot != null && theRoot.getLeft() == null && theRoot.getRight() == null;
}
public STDAVLNodeDCAS<K, V> getLockFreeMap(){
return lockFreeHolder;
}
public int size(){
return size;
}
public boolean isEmpty(){
return getRoot() == null;
}
public boolean containsKey(Object key){
return getSTDAVLNodeDCAS(key) != null;
}
public V get(Object key){
STDAVLNodeDCAS<K,V> node = getSTDAVLNodeDCAS(key);
if(node != null){
return node.getValue();
}else{
return null;
}
}
public V put(K key, V value){
return put(key, value, true);
}
public V putIfAbsent(K key, V value) {
return put(key, value, false);
}
public V remove(Object keyParam){
boolean dirLeft = true;
if(DEBUG) avlValidateP(getRoot());
STDAVLNodeDCAS<K,V> currentNode = getRoot();
if(comparator != null){
@SuppressWarnings("unchecked")
K key = (K)keyParam;
Comparator<? super K> cpr = comparator;
while(currentNode != null){
K nodeKey = currentNode.key;
int compareValue = cpr.compare(key, nodeKey);
if(compareValue < 0) {
dirLeft = true;
currentNode = currentNode.getLeft();
} else if (compareValue > 0) {
dirLeft = false;
currentNode = currentNode.getRight();
} else {
size = size - 1;
break;
}
}
}else{
@SuppressWarnings("unchecked")
Comparable<? super K> key = (Comparable<? super K>) keyParam;
while(currentNode != null){
K nodeKey = currentNode.key;
int compareValue = key.compareTo(nodeKey);
if(compareValue < 0) {
dirLeft = true;
currentNode = currentNode.getLeft();
} else if (compareValue > 0) {
dirLeft = false;
currentNode = currentNode.getRight();
} else {
size = size - 1;
break;
}
}
}
V toReturn = null;
if(currentNode == null){
if(DEBUG) avlValidateP(getRoot());
return null;
}else{
toReturn = currentNode.getValue();
}
//Fix balance
STDAVLNodeDCAS<K,V> prevNode = currentNode.parent;
boolean continueFix = true;
if(currentNode.getLeft() == null){
if(prevNode == null){
setRoot(currentNode.getRight());
}else if(dirLeft){
prevNode.setLeft(currentNode.getRight());
}else{
prevNode.setRight(currentNode.getRight());
}
if(currentNode.getRight() != null){
currentNode.getRight().parent = prevNode;
}
currentNode = currentNode.getRight();
}else if(currentNode.getRight() == null){
if(prevNode == null){
setRoot(currentNode.getLeft());
}else if(dirLeft){
prevNode.setLeft(currentNode.getLeft());
}else{
prevNode.setRight(currentNode.getLeft());
}
if(currentNode.getLeft() != null){
currentNode.getLeft().parent = prevNode;
}
currentNode = currentNode.getLeft();
}else{
if(prevNode == null){
continueFix = replaceWithRightmost(currentNode);
STDAVLNodeDCAS<K,V> r = getRoot();
currentNode = r.getLeft();
prevNode = r;
}else if(prevNode.getLeft() == currentNode){
continueFix = replaceWithRightmost(currentNode);
prevNode = prevNode.getLeft();
currentNode = prevNode.getLeft();
dirLeft = true;
}else{
continueFix = replaceWithRightmost(currentNode);
prevNode = prevNode.getRight();
currentNode = prevNode.getLeft();
dirLeft = true;
}
}
// current node is the node we are coming from
// prev node is the node that needs re-balancing
while (continueFix && prevNode != null) {
STDAVLNodeDCAS<K,V> nextPrevNode = prevNode.parent;
if(nextPrevNode != null){
boolean findCurrentLeftDir = true;
if(nextPrevNode.getLeft() == prevNode){
findCurrentLeftDir = true;
}else{
findCurrentLeftDir = false;
}
if(currentNode == null){
if (dirLeft) {
continueFix = deleteBalanceLeft(prevNode);
} else {
continueFix = deleteBalanceRight(prevNode);
}
}else{
if (prevNode.getLeft() == currentNode) {
continueFix = deleteBalanceLeft(prevNode);
} else {
continueFix = deleteBalanceRight(prevNode);
}
}
if(findCurrentLeftDir){
currentNode = nextPrevNode.getLeft();
}else{
currentNode = nextPrevNode.getRight();
}
prevNode = nextPrevNode;
}else{
if(currentNode == null){
if (dirLeft) {
continueFix = deleteBalanceLeft(prevNode);
} else {
continueFix = deleteBalanceRight(prevNode);
}
}else{
if (prevNode.getLeft() == currentNode) {
continueFix = deleteBalanceLeft(prevNode);
} else {
continueFix = deleteBalanceRight(prevNode);
}
}
prevNode = null;
}
}
if(DEBUG) avlValidateP(getRoot());
return toReturn;
}
public void clear(){
size = 0;
setRoot(null);
}
public Set<Map.Entry<K, V>> entrySet(){
ArrayList<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>();
addAllToList(getRoot(), list);
return new HashSet<Map.Entry<K, V>>(list);
}
}