/******************************************************************************
* Copyright (c) 2009 - 2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
/**
*
*/
package data.concurrent;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* A Java implementation of the non-blocking concurrent list by Harris.
* For simplicity, the implementation accepts integer keys.
* @author Emina Torlak
*/
public final class HarrisList {
private final Node head, tail;
/**
* Constructs an empty Harris list.
*/
public HarrisList() {
head = new Node(Short.MIN_VALUE);
tail = new Node(Short.MAX_VALUE);
head.next.set(tail);
}
/**
* Adds the given key to this list if not already present.
* Returns true if the list was modified as a result of
* this operation; otherwise returns false.
* @return true if the list was modified as a result of
* this operation; otherwise returns false.
*/
public boolean insert(int key) {
Node newNode = new Node(key);
Node rightNode, leftNode;
do {
Pair pair = search(key);
leftNode = pair.fst;
rightNode = pair.snd;
if (rightNode != tail && rightNode.key==key) {
return false;
}
newNode.next = new AtomicReference/*<Node>*/(rightNode);
if (leftNode.next.compareAndSet(rightNode, newNode))
return true;
} while(true);
}
/**
* Removes the given key from this list, if present.
* Returns true if the list was modified as a result of
* this operation; otherwise returns false.
* @return true if the list was modified as a result of
* this operation; otherwise returns false.
*/
public boolean delete(int key) {
Node rightNode, rightNodeNext, leftNode;
do {
Pair pair = search(key);
leftNode = pair.fst;
rightNode = pair.snd;
if (rightNode==tail || rightNode.key!=key) {
return false;
}
rightNodeNext = rightNode.next();
if (!rightNode.isMarked()) {
if (rightNode.isMarked.compareAndSet(false, true)) {
break;
}
}
} while(true);
if (!leftNode.next.compareAndSet(rightNode, rightNodeNext)) {
search(rightNode.key);
}
return true;
}
/**
* Returns a pair of adjacent unmarked nodes
* such that the key of the left node is less
* than searchKey and the key of the right node
* is greater than or equal to searchKey.
* @return a pair of adjacent unmarked nodes
* such that the key of the left node is less
* than searchKey and the key of the right node
* is greater than or equal to searchKey.
*/
private Pair/*<Node, Node>*/ search(int searchKey) {
Node leftNode = null, leftNodeNext = null, rightNode = null;
search_again:
do {
Node t = head;
Node tNext = head.next();
do {
if (!t.isMarked.get()) {
leftNode = t;
leftNodeNext = t.next();
}
t = tNext;
if (t==tail) break;
tNext = t.next();
} while (t.isMarked() || t.key < searchKey);
rightNode = t;
if (leftNodeNext == rightNode) {
if (rightNode != tail && rightNode.isMarked())
continue search_again;
else
return new Pair(leftNode,rightNode);
}
if (leftNode.next.compareAndSet(leftNodeNext, rightNode)) {
if (rightNode != tail && rightNode.isMarked())
continue search_again;
else
return new Pair(leftNode,rightNode);
}
} while (true);
}
/**
* Node in the linked list.
*/
private static final class Node {
int key;
AtomicReference/*<Node>*/ next;
AtomicBoolean isMarked;
Node(int key) {
this.key = key;
this.next = new AtomicReference/*<Node>*/();
this.isMarked = new AtomicBoolean(false);
}
boolean isMarked() { return isMarked.get(); }
Node next() { return (Node)next.get(); }
}
/**
* A pair of nodes.
* @author Emina Torlak
*/
private static final class Pair {
final Node fst;
final Node snd;
Pair(Node fst, Node snd) {
this.fst = fst;
this.snd = snd;
}
}
}