/*
* Algorithm:
* Fine-grained locking skip list.
* "A Simple Optimistic Skiplist Algorithm"
* M. Herlihy, Y. Lev, V. Luchangco, N. Shavit
* p.124-138, SIROCCO 2007
*
* Code:
* Based on example code from:
* "The Art of Multiprocessor Programming"
* M. Herlihy, N. SHavit
* chapter 14.3, 2008
*
*/
package skiplists.lockbased;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import contention.abstractions.CompositionalIntSet;
import contention.abstractions.CompositionalIterator;
public final class LazySkipList implements CompositionalIntSet {
/** The maximum number of levels */
final private int maxLevel;
/** The first element of the list */
final private Node head;
/** The last element of the list */
final private Node tail;
/** The thread-private PRNG, used for fil(), not for height/level determination. */
final private static ThreadLocal<Random> s_random = new ThreadLocal<Random>() {
@Override
protected synchronized Random initialValue() {
return new Random();
}
};
private int randomLevel() {
return Math.min((maxLevel - 1), (skiplists.RandomLevelGenerator.randomLevel()));
}
public LazySkipList() {
this(31);
}
public LazySkipList(final int maxLevel) {
this.head = new Node(Integer.MIN_VALUE, maxLevel);
this.tail = new Node(Integer.MAX_VALUE, maxLevel);
this.maxLevel = maxLevel;
for (int i = 0; i <= maxLevel; i++) {
head.next[i] = tail;
}
}
@Override
public boolean containsInt(final int value) {
Node[] preds = (Node[]) new Node[maxLevel + 1];
Node[] succs = (Node[]) new Node[maxLevel + 1];
int levelFound = find(value, preds, succs);
return (levelFound != -1 && succs[levelFound].fullyLinked && !succs[levelFound].marked);
}
/* The preds[] and succs[] arrays are filled from the maximum level to 0 with the predecessor and successor references for the given key. */
private int find(final int value, Node[] preds, Node[] succs) {
int key = value;
int levelFound = -1;
Node pred = head;
for (int level = maxLevel; level >= 0; level--) {
Node curr = pred.next[level];
while (key > curr.key) {
pred = curr;
curr = pred.next[level];
}
if (levelFound == -1 && key == curr.key) {
levelFound = level;
}
preds[level] = pred;
succs[level] = curr;
}
return levelFound;
}
@Override
public boolean addInt(final int value) {
int topLevel = randomLevel();
Node[] preds = (Node[]) new Node[maxLevel+ 1];
Node[] succs = (Node[]) new Node[maxLevel+ 1];
while (true) {
/* Call find() to initialize preds and succs. */
int levelFound = find(value, preds, succs);
/* If an node is found that is unmarked then return false. */
if (levelFound != -1) {
Node nodeFound = succs[levelFound];
if (!nodeFound.marked) {
/* Needs to wait for nodes to become fully linked. */
while (!nodeFound.fullyLinked) {}
return false;
}
/* If marked another thread is deleting it, so we retry. */
continue;
}
int highestLocked = -1;
try {
Node pred, succ;
boolean valid = true;
/* Acquire locks. */
for (int level = 0; valid && (level <= topLevel); level++) {
pred = preds[level];
succ = succs[level];
pred.lock.lock();
highestLocked = level;
valid = !pred.marked && !succ.marked && pred.next[level]==succ;
}
/* Must have encountered effects of a conflicting method, so it releases (in the
* finally block) the locks it acquired and retries */
if (!valid) {
continue;
}
Node newNode = new Node(value, topLevel);
for (int level = 0; level <= topLevel; level++) {
newNode.next[level] = succs[level];
}
for (int level = 0; level <= topLevel; level++) {
preds[level].next[level] = newNode;
}
newNode.fullyLinked = true; // successful and linearization point
return true;
} finally {
for (int level = 0; level <= highestLocked; level++) {
preds[level].unlock();
}
}
}
}
@Override
public boolean removeInt(final int value) {
Node victim = null;
boolean isMarked = false;
int topLevel = -1;
Node[] preds = (Node[]) new Node[maxLevel + 1];
Node[] succs = (Node[]) new Node[maxLevel + 1];
while (true) {
/* Call find() to initialize preds and succs. */
int levelFound = find(value, preds, succs);
if (levelFound != -1) {
victim = succs[levelFound];
}
/* Ready to delete if unmarked, fully linked, and at its top level. */
if (isMarked | (levelFound != -1 && (victim.fullyLinked && victim.topLevel == levelFound && !victim.marked))) {
/* Acquire locks in order to logically delete. */
if (!isMarked) {
topLevel = victim.topLevel;
victim.lock.lock();
if (victim.marked) {
victim.lock.unlock();
return false;
}
victim.marked = true; // logical deletion
isMarked = true;
}
int highestLocked = -1;
try {
Node pred, succ;
boolean valid = true;
/* Acquire locks. */
for (int level = 0; valid && (level <= topLevel); level++) {
pred = preds[level];
pred.lock.lock();
highestLocked = level;
valid = !pred.marked && pred.next[level]==victim;
}
/* Pred has changed and is no longer suitable, thus unlock and retries. */
if (!valid) {
continue;
}
/* Unlink. */
for (int level = topLevel; level >= 0; level--) {
preds[level].next[level] = victim.next[level];
}
victim.lock.unlock();
return true;
} finally {
for (int i = 0; i <= highestLocked; i++) {
preds[i].unlock();
}
}
} else {
return false;
}
}
}
@Override
public void fill(final int range, final long size) {
while (this.size() < size) {
this.addInt(s_random.get().nextInt(range));
}
}
@Override
public Object getInt(int value) {
// TODO
return null;
}
@Override
public boolean addAll(Collection<Integer> c) {
// TODO
return false;
}
@Override
public boolean removeAll(Collection<Integer> c){
// TODO
return false;
}
@Override
public int size() {
int size = 0;
Node node = head.next[0].next[0];
while (node != null) {
node = node.next[0];
size++;
}
return size;
}
@Override
public void clear() {
for (int i = 0; i <= this.maxLevel; i++) {
this.head.next[i] = this.tail;
}
return;
}
@Override
public String toString() {
// TODO
return null;
}
@Override
public Object putIfAbsent(int x, int y) {
// TODO
return null;
}
private static final class Node {
final Lock lock = new ReentrantLock();
final int key;
final Node[] next;
volatile boolean marked = false;
volatile boolean fullyLinked = false;
private int topLevel;
public Node(final int value, int height) {
key = value;
next = new Node[height + 1];
topLevel = height;
}
public void lock() {
lock.lock();
}
public void unlock() {
lock.unlock();
}
}
}