/**
* This is an implementation of the non-blocking, concurrent binary search tree of
* Faith Ellen, Panagiota Fatourou, Eric Ruppert and Franck van Breugel.
*
* Copyright (C) 2011 Trevor Brown, Joanna Helga
* Contact Trevor Brown (tabrown@cs.toronto.edu) with any questions or comments.
*
* 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/>.
*/
package trees.lockfree;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import contention.abstractions.CompositionalMap;
public class NonBlockingTorontoBSTMap<K extends Comparable<? super K>, V>
implements CompositionalMap<K, V> {
// --------------------------------------------------------------------------------
// Class: Node
// --------------------------------------------------------------------------------
protected final static class Node<E extends Comparable<? super E>, V> {
final E key;
final V value;
volatile Node<E, V> left;
volatile Node<E, V> right;
volatile Info<E, V> info;
/** FOR MANUAL CREATION OF NODES (only used directly by testbed) **/
Node(final E key, final V value, final Node<E, V> left,
final Node<E, V> right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.info = null;
}
/** TO CREATE A LEAF NODE **/
Node(final E key, final V value) {
this(key, value, null, null);
}
/** TO CREATE AN INTERNAL NODE **/
Node(final E key, final Node<E, V> left, final Node<E, V> right) {
this(key, null, left, right);
}
}
// --------------------------------------------------------------------------------
// Class: Info, DInfo, IInfo, Mark, Clean
// May 25th: trying to make CAS to update field static
// instead of using <state, Info>, we extends Info to all 4 states
// to see a state of a node, see what kind of Info class it has
// --------------------------------------------------------------------------------
protected static abstract class Info<E extends Comparable<? super E>, V> {
}
protected final static class DInfo<E extends Comparable<? super E>, V>
extends Info<E, V> {
final Node<E, V> p;
final Node<E, V> l;
final Node<E, V> gp;
final Info<E, V> pinfo;
DInfo(final Node<E, V> leaf, final Node<E, V> parent,
final Node<E, V> grandparent, final Info<E, V> pinfo) {
this.p = parent;
this.l = leaf;
this.gp = grandparent;
this.pinfo = pinfo;
}
}
protected final static class IInfo<E extends Comparable<? super E>, V>
extends Info<E, V> {
final Node<E, V> p;
final Node<E, V> l;
final Node<E, V> newInternal;
IInfo(final Node<E, V> leaf, final Node<E, V> parent,
final Node<E, V> newInternal) {
this.p = parent;
this.l = leaf;
this.newInternal = newInternal;
}
}
protected final static class Mark<E extends Comparable<? super E>, V>
extends Info<E, V> {
final DInfo<E, V> dinfo;
Mark(final DInfo<E, V> dinfo) {
this.dinfo = dinfo;
}
}
protected final static class Clean<E extends Comparable<? super E>, V>
extends Info<E, V> {
}
// --------------------------------------------------------------------------------
// DICTIONARY
// --------------------------------------------------------------------------------
private static final AtomicReferenceFieldUpdater<Node, Node> leftUpdater = AtomicReferenceFieldUpdater
.newUpdater(Node.class, Node.class, "left");
private static final AtomicReferenceFieldUpdater<Node, Node> rightUpdater = AtomicReferenceFieldUpdater
.newUpdater(Node.class, Node.class, "right");
private static final AtomicReferenceFieldUpdater<Node, Info> infoUpdater = AtomicReferenceFieldUpdater
.newUpdater(Node.class, Info.class, "info");
final Node<K, V> root;
public NonBlockingTorontoBSTMap() {
// to avoid handling special case when <= 2 nodes,
// create 2 dummy nodes, both contain key null
// All real keys inside BST are required to be non-null
root = new Node<K, V>(null, new Node<K, V>(null, null), new Node<K, V>(
null, null));
}
public NonBlockingTorontoBSTMap(final Node<K, V> root) {
this.root = root;
}
// --------------------------------------------------------------------------------
// PUBLIC METHODS:
// - find : boolean
// - insert : boolean
// - delete : boolean
// --------------------------------------------------------------------------------
/** PRECONDITION: k CANNOT BE NULL **/
public final boolean containsKey(final K key) {
if (key == null)
throw new NullPointerException();
Node<K, V> l = root.left;
while (l.left != null) {
l = (l.key == null || key.compareTo(l.key) < 0) ? l.left : l.right;
}
return (l.key != null && key.compareTo(l.key) == 0) ? true : false;
}
/** PRECONDITION: k CANNOT BE NULL **/
@Override
public final V get(final Object k) {
K key = (K) k;
if (key == null)
throw new NullPointerException();
Node<K, V> l = root.left;
while (l.left != null) {
l = (l.key == null || key.compareTo(l.key) < 0) ? l.left : l.right;
}
return (l.key != null && key.compareTo(l.key) == 0) ? l.value : null;
}
// Insert key to dictionary, returns the previous value associated with the
// specified key,
// or null if there was no mapping for the key
/** PRECONDITION: k CANNOT BE NULL **/
@Override
public final V putIfAbsent(final K key, final V value) {
Node<K, V> newInternal;
Node<K, V> newSibling, newNode;
/** SEARCH VARIABLES **/
Node<K, V> p;
Info<K, V> pinfo;
Node<K, V> l;
/** END SEARCH VARIABLES **/
newNode = new Node<K, V>(key, value);
while (true) {
/** SEARCH **/
p = root;
pinfo = p.info;
l = p.left;
while (l.left != null) {
p = l;
l = (l.key == null || key.compareTo(l.key) < 0) ? l.left
: l.right;
}
pinfo = p.info; // read pinfo once instead of every iteration
if (l != p.left && l != p.right)
continue; // then confirm the child link to l is valid
// (just as if we'd read p's info field before the
// reference to l)
/** END SEARCH **/
if (key.equals(l.key)) {
return l.value; // key already in the tree, no duplicate allowed
} else if (!(pinfo == null || pinfo.getClass() == Clean.class)) {
help(pinfo);
} else {
newSibling = new Node<K, V>(l.key, l.value);
if (l.key == null || key.compareTo(l.key) < 0) // newinternal =
// max(ret.l.key,
// key);
newInternal = new Node<K, V>(l.key, newNode, newSibling);
else
newInternal = new Node<K, V>(key, newSibling, newNode);
final IInfo<K, V> newPInfo = new IInfo<K, V>(l, p, newInternal);
// try to IFlag parent
if (infoUpdater.compareAndSet(p, pinfo, newPInfo)) {
helpInsert(newPInfo);
return null;
} else {
// if fails, help the current operation
// [CHECK]
// need to get the latest p.info since CAS doesnt return
// current value
help(p.info);
}
}
}
}
// Insert key to dictionary, return the previous value associated with the
// specified key,
// or null if there was no mapping for the key
/** PRECONDITION: k CANNOT BE NULL **/
public final V put(final K key, final V value) {
Node<K, V> newInternal;
Node<K, V> newSibling, newNode;
IInfo<K, V> newPInfo;
V result;
/** SEARCH VARIABLES **/
Node<K, V> p;
Info<K, V> pinfo;
Node<K, V> l;
/** END SEARCH VARIABLES **/
newNode = new Node<K, V>(key, value);
while (true) {
/** SEARCH **/
p = root;
pinfo = p.info;
l = p.left;
while (l.left != null) {
p = l;
l = (l.key == null || key.compareTo(l.key) < 0) ? l.left
: l.right;
}
pinfo = p.info; // read pinfo once instead of every iteration
if (l != p.left && l != p.right)
continue; // then confirm the child link to l is valid
// (just as if we'd read p's info field before the
// reference to l)
/** END SEARCH **/
if (!(pinfo == null || pinfo.getClass() == Clean.class)) {
help(pinfo);
} else {
if (key.equals(l.key)) {
// key already in the tree, try to replace the old node with
// new node
newPInfo = new IInfo<K, V>(l, p, newNode);
result = l.value;
} else {
// key is not in the tree, try to replace a leaf with a
// small subtree
newSibling = new Node<K, V>(l.key, l.value);
if (l.key == null || key.compareTo(l.key) < 0) // newinternal
// =
// max(ret.l.key,
// key);
{
newInternal = new Node<K, V>(l.key, newNode, newSibling);
} else {
newInternal = new Node<K, V>(key, newSibling, newNode);
}
newPInfo = new IInfo<K, V>(l, p, newInternal);
result = null;
}
// try to IFlag parent
if (infoUpdater.compareAndSet(p, pinfo, newPInfo)) {
helpInsert(newPInfo);
return result;
} else {
// if fails, help the current operation
// need to get the latest p.info since CAS doesnt return
// current value
help(p.info);
}
}
}
}
// Delete key from dictionary, return the associated value when successful,
// null otherwise
/** PRECONDITION: k CANNOT BE NULL **/
@Override
public final V remove(final Object k) {
K key = (K) k;
/** SEARCH VARIABLES **/
Node<K, V> gp;
Info<K, V> gpinfo;
Node<K, V> p;
Info<K, V> pinfo;
Node<K, V> l;
/** END SEARCH VARIABLES **/
while (true) {
/** SEARCH **/
gp = null;
gpinfo = null;
p = root;
pinfo = p.info;
l = p.left;
while (l.left != null) {
gp = p;
p = l;
l = (l.key == null || key.compareTo(l.key) < 0) ? l.left
: l.right;
}
// note: gp can be null here, because clearly the root.left.left ==
// null
// when the tree is empty. however, in this case, l.key will be
// null,
// and the function will return null, so this does not pose a
// problem.
if (gp != null) {
gpinfo = gp.info; // - read gpinfo once instead of every
// iteration
if (p != gp.left && p != gp.right)
continue; // then confirm the child link to p is valid
pinfo = p.info; // (just as if we'd read gp's info field before
// the reference to p)
if (l != p.left && l != p.right)
continue; // - do the same for pinfo and l
}
/** END SEARCH **/
if (!key.equals(l.key))
return null;
if (!(gpinfo == null || gpinfo.getClass() == Clean.class)) {
help(gpinfo);
} else if (!(pinfo == null || pinfo.getClass() == Clean.class)) {
help(pinfo);
} else {
// try to DFlag grandparent
final DInfo<K, V> newGPInfo = new DInfo<K, V>(l, p, gp, pinfo);
if (infoUpdater.compareAndSet(gp, gpinfo, newGPInfo)) {
if (helpDelete(newGPInfo))
return l.value;
} else {
// if fails, help grandparent with its latest info value
help(gp.info);
}
}
}
}
// --------------------------------------------------------------------------------
// PRIVATE METHODS
// - helpInsert
// - helpDelete
// --------------------------------------------------------------------------------
private void helpInsert(final IInfo<K, V> info) {
(info.p.left == info.l ? leftUpdater : rightUpdater).compareAndSet(
info.p, info.l, info.newInternal);
infoUpdater.compareAndSet(info.p, info, new Clean());
}
private boolean helpDelete(final DInfo<K, V> info) {
final boolean result;
result = infoUpdater.compareAndSet(info.p, info.pinfo, new Mark<K, V>(
info));
final Info<K, V> currentPInfo = info.p.info;
// if CAS succeed or somebody else already suceed helping, the
// helpMarked
if (result
|| (currentPInfo.getClass() == Mark.class && ((Mark<K, V>) currentPInfo).dinfo == info)) {
helpMarked(info);
return true;
} else {
help(currentPInfo);
infoUpdater.compareAndSet(info.gp, info, new Clean());
return false;
}
}
private void help(final Info<K, V> info) {
if (info.getClass() == IInfo.class)
helpInsert((IInfo<K, V>) info);
else if (info.getClass() == DInfo.class)
helpDelete((DInfo<K, V>) info);
else if (info.getClass() == Mark.class)
helpMarked(((Mark<K, V>) info).dinfo);
}
private void helpMarked(final DInfo<K, V> info) {
final Node<K, V> other = (info.p.right == info.l) ? info.p.left
: info.p.right;
(info.gp.left == info.p ? leftUpdater : rightUpdater).compareAndSet(
info.gp, info.p, other);
infoUpdater.compareAndSet(info.gp, info, new Clean());
}
/**
*
* DEBUG CODE (FOR TESTBED)
*
*/
/**
* If size() returns -1, then concurrent access prevented its successful
* read. To ensure it returns the tree size, call it using the pattern:
* <code>
* while((sz = tree.size()) == -1) {}
* </code> This allows the caller to decide whether it should block.
*/
@Override
public final int size() {
Node root = getSnapshot();
if (root == null)
return -1;
return sequentialSize(root);
}
@Override
public final NonBlockingTorontoBSTMap clone() {
Node newroot = null;
for (;;)
if ((newroot = getSnapshot()) != null)
return new NonBlockingTorontoBSTMap(newroot);
}
/**
* WARNING: This assumes that there are no concurrent accesses occurring. If
* concurrent accesses can occur, use size() (or this on a clone()).
*/
public int sequentialSize(final Node node) {
if (node == null)
return 0;
if (node.left == null && node.key != null)
return 1;
return sequentialSize(node.left) + sequentialSize(node.right);
}
private void readRefs(final Node node,
final HashMap<Node, Pair<Node, Node>> refs) {
if (node == null)
return;
refs.put(node, new Pair<Node, Node>(node.left, node.right));
readRefs(node.left, refs);
readRefs(node.right, refs);
}
private boolean checkRefs(final Node node,
final HashMap<Node, Pair<Node, Node>> refs) {
if (node == null)
return true;
Pair<Node, Node> p = refs.get(node);
if (!p.equals(new Pair<Node, Node>(node.left, node.right)))
return false;
return checkRefs(p.key, refs) && checkRefs(p.value, refs);
}
private Node buildRefs(final Node node,
final HashMap<Node, Pair<Node, Node>> refs) {
if (node == null)
return null;
Pair<Node, Node> children = refs.get(node);
return new Node(node.key, node.value, buildRefs(children.key, refs),
buildRefs(children.value, refs));
}
private Node getSnapshot() {
final HashMap<Node, Pair<Node, Node>> refs = new HashMap<Node, Pair<Node, Node>>();
readRefs(root, refs);
if (!checkRefs(root, refs))
return null;
return buildRefs(root, refs);
}
class Pair<K, V> {
K key;
V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public boolean equals(Object o) {
if (o == null || !o.getClass().equals(getClass()))
return false; // CAN DO AWAY WITH THIS AT THE COST OF TYPE
// SAFETY
Pair<K, V> p = (Pair<K, V>) o;
return p.key == key && p.value == value;
}
}
private int sumDepths(Node node, int depth) {
if (node == null)
return 0;
return depth + sumDepths(node.left, depth + 1)
+ sumDepths(node.right, depth + 1);
}
public final int getSumOfDepths() {
return sumDepths(root, 0);
}
/**
* WARNING: This assumes that there are no concurrent accesses occurring. If
* concurrent accesses can occur, use size() (or this on a clone()).
*/
public final int sequentialSize() {
return sequentialSize(root);
}
@Override
public boolean containsKey(Object key) {
// TODO Auto-generated method stub
return false;
}
@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() {
return (this.size() == 0) ? true: false;
}
@Override
public Set<K> keySet() {
// 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;
}
@Override
public void clear() {
// root = new Node<K, V>(null, new Node<K, V>(null, null), new Node<K,
// V>(
// null, null));
root.left = new Node<K, V>(null, null);
root.right = new Node<K, V>(null, null);
root.info = null;
}
}