package linkedlists.lockbased;
import contention.abstractions.AbstractCompositionalIntSet;
/**
* The Versioned Linked List using the StampedLock from Java 8 as in:
*
* A Concurrency-Optimal List-Based Set. Gramoli, Kuznetsov, Ravi, Shang. 2015.
*
* @author Di Shang
*/
public class VersionedListSetStampLock extends AbstractCompositionalIntSet {
// sentinel nodes
final private NodeStampLock head;
final private NodeStampLock tail;
static final int ABORT = 1;
static final int OK = 0;
public VersionedListSetStampLock() {
// init sentinel values
tail = new NodeStampLock(Integer.MAX_VALUE, null);
head = new NodeStampLock(Integer.MIN_VALUE, tail);
}
public class Window {
public NodeStampLock prev = head;
public NodeStampLock curr;
public long prevVersion;
public Window() {
}
public void setValues(NodeStampLock prev, NodeStampLock curr) {
this.prev = prev;
this.curr = curr;
}
}
private int validate(Window win) {
win.prevVersion = win.prev.getVersion();
if (win.prev.isDeleted || win.prev.next != win.curr) {
return ABORT;
}
return OK;
}
/**
* Traverse the linked list up to a node with value >= the value being searched for
*
* @param value
* the value being searched for
* @return a window of focus around the located node
*/
public Window traverse(final int value, final Window win) {
NodeStampLock prev = win.prev, curr;
if (prev.isDeleted) {
prev = head;
}
curr = prev.next;
while (curr.value < value) {
prev = curr;
curr = curr.next;
}
win.setValues(prev, curr);
return win;
}
/*
* Insert
*
* @see contention.abstractions.CompositionalIntSet#addInt(int)
*/
@Override
public boolean addInt(int v) {
Window window = new Window();
NodeStampLock newNode = null;
long stamp;
// keep restarting upon abort
while (true) {
window = traverse(v, window);
if (window.curr.isDeleted) {
continue; // abort
}
// value already exist, operation fail
if (window.curr.value == v) {
return false;
}
if (validate(window) == ABORT) {
continue; // abort
}
if (newNode == null) {
newNode = new NodeStampLock(v);
}
newNode.next = window.curr;
// ------------------------ critical section ---------------------
// try to lock prev at the versions we just read
if ((stamp = window.prev.tryLockAtVersion(window.prevVersion)) == 0) {
continue; // abort
}
// update prev to point to the new node
window.prev.next = newNode;
window.prev.unlockAndIncrementVersion(stamp);
// ------------------------ critical section ---------------------
return true;
}
}
/*
* Remove
*
* @see contention.abstractions.CompositionalIntSet#removeInt(int)
*/
@Override
public boolean removeInt(int v) {
Window window = new Window();
long stamp, stamp2;
// keep restarting upon abort
while (true) {
window = traverse(v, window);
// value not exist or already deleted, operation fail
if (window.curr.value != v || window.curr.isDeleted) {
return false;
}
if (validate(window) == ABORT) {
continue; // abort
}
// ------------------------ critical section ---------------------
// try to lock both prev and curr at the versions we just read
if ((stamp = window.prev.tryLockAtVersion(window.prevVersion)) == 0) {
continue; // abort
}
stamp2 = window.curr.lock();
// mark curr as deleted
window.curr.isDeleted = true;
// update prev to point to next
window.prev.next = window.curr.next;
window.curr.unlockAndIncrementVersion(stamp2);
window.prev.unlockAndIncrementVersion(stamp);
// ------------------------ critical section ---------------------
return true;
}
}
@Override
public boolean containsInt(int v) {
NodeStampLock curr = head;
while (curr.value < v) {
curr = curr.next;
}
return (curr.value == v) && (!curr.isDeleted);
}
/**
* Non atomic and thread-unsafe
*/
@Override
public int size() {
int count = 0;
NodeStampLock curr = head.next;
while (curr != tail) {
curr = curr.next;
count++;
}
return count;
}
@Override
public void clear() {
head.next = tail;
head.resetLock();
tail.resetLock();
}
}