package linkedlists.lockbased.lazyutils;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @author Erik Sapir & Lior Zibi
*
* Lazy list implementation: lock-free contains method.
* removed element are first removed logically and only than physically
*
* @param T Item type.
*/
public class LazyList<T>{
/**
* First list Node
*/
volatile private Node m_head;
/**
* counter for number of element in the list
*/
volatile private SnapshotCounter m_counter;
/**
* Constructor
*/
public LazyList(int numOfThreads) {
// Add sentinels to start and end
this.m_head = new Node(Integer.MIN_VALUE);
this.m_head.m_next = new Node(Integer.MAX_VALUE);
m_counter = new SnapshotCounter(numOfThreads);
}
public boolean add(T item, int threadID) {
int key = item.hashCode();
while (true) {
Node pred = this.m_head;
Node curr = m_head.m_next;
//search for first element equal or greater than the new element
while (curr.m_key < key) {
pred = curr; curr = curr.m_next;
}
pred.lock();
try {
curr.lock();
try {
//validate theat pred and curr are not deleted and
//that pred.next = curr
if (validate(pred, curr)) {
if (curr.m_key == key) {
// element is already in list - nothing to add
return false;
} else {
// add new element to list
Node Node = new Node(item);
Node.m_next = curr;
pred.m_next = Node;
//element added successfully - increment number of element in list
m_counter.inc(threadID);
return true;
}
}
} finally {
curr.unlock(); // always unlock
}
} finally {
pred.unlock(); // always unlock
}
}
}
public boolean remove(T item, int threadID) {
int key = item.hashCode();
while (true) {
Node pred = this.m_head;
Node curr = m_head.m_next;
//search for first element equal or greater than element to be removed
while (curr.m_key < key) {
pred = curr; curr = curr.m_next;
}
pred.lock();
try {
curr.lock();
try {
if (validate(pred, curr)) {
if (curr.m_key != key) {
// element was not found in list - nothing to remove
return false;
} else {
//element was found in list - first logically remove, than
//physically remove
curr.marked = true; // logically remove
pred.m_next = curr.m_next; // physically remove
//element removed successfully - decrement number of elements in list
m_counter.dec(threadID);
return true;
}
}
} finally { // always unlock curr
curr.unlock();
}
} finally { // always unlock pred
pred.unlock();
}
}
}
public boolean contains(T item, int threadID) {
int key = item.hashCode();
Node curr = this.m_head;
//search for first element equal or greater than element we are searching
while (curr.m_key < key)
curr = curr.m_next;
//return true iff found element in list and it is not logically removed
return curr.m_key == key && !curr.marked;
}
public void clean(int threadID) {
m_head.lock();
Node curr = m_head.m_next;
try {
//go over each and every element in list and remove it.
while (curr.m_key != Integer.MAX_VALUE){
curr.lock();
try {
curr.marked = true; // logically remove
m_head.m_next = curr.m_next; // physically remove
//element removed successfully - decrement number of elements in list
m_counter.dec(threadID);
} finally {
curr.unlock(); //always unlock
}
curr = m_head.m_next;
}
} finally {
m_head.unlock(); //always unlock
}
}
public boolean isEmpty(int threadID) {
Node curr = this.m_head;
//search an element that exist in the list, both logically and physically
while (curr.m_key != Integer.MAX_VALUE) {
//make sure element was not removed logically
if (!curr.marked)
return false;
curr = curr.m_next;
}
return true;
}
public int size(int threadID) {
return m_counter.scan_sum();
}
/**
* Check that prev and curr are still in list and adjacent
*/
private boolean validate(Node pred, Node curr) {
return !pred.marked && !curr.marked && pred.m_next == curr;
}
/**
* list Node
*/
private class Node {
/**
* actual item
*/
final T m_item;
/**
* item's hash code
*/
final int m_key;
/**
* next Node in list
*/
volatile Node m_next;
/**
* If true, Node is logically deleted.
*/
volatile boolean marked;
/**
* Synchronizes Node.
*/
Lock m_lock;
/**
* Constructor for usual Node
* @param item element in list
*/
Node(T item) { // usual constructor
this.m_item = item;
this.m_key = item.hashCode();
this.m_next = null;
this.marked = false;
this.m_lock = new ReentrantLock();
}
/**
* Constructor for sentinel Node
* @param key should be min or max int value
*/
Node(int key) { // sentinel constructor
this.m_item = null;
this.m_key = key;
this.m_next = null;
this.marked = false;
this.m_lock = new ReentrantLock();
}
/**
* Lock Node
*/
void lock() {m_lock.lock();}
/**
* Unlock Node
*/
void unlock() {m_lock.unlock();}
}
}