package queues.transactional;
import java.util.Collection;
import java.util.Random;
import org.deuce.Atomic;
import contention.abstractions.CompositionalIntSet;
import contention.benchmark.Parameters;
/**
* A transactional Queue implemented as a linked list
* that also exports an integer set interface.
*
* @author Vincent Gramoli
*
*/
public class CompositionalQueueIntSet implements CompositionalIntSet {
private Node m_first;
private Node m_last;
/** The thread-private PRNG */
final private static ThreadLocal<Random> s_random = new ThreadLocal<Random>() {
@Override
protected synchronized Random initialValue() {
return new Random();
}
};
public CompositionalQueueIntSet() {
m_first = new Node(0);
m_last = m_first;
}
public void fill(final int range, final long size) {
while (this.size() < size) {
this.addInt(s_random.get().nextInt(range));
}
}
@Atomic(metainf = "regular")
public boolean addInt(int value) {
push(value);
return true;
}
/**
* To respect the semantics of ConcurrentLinkedQueue
* returns true as soon as a modif occurs
*/
@Override
@Atomic(metainf = "regular")
public boolean addAll(Collection<Integer> C) {
boolean result = false;
for (int x : C) result |= this.addInt(x);
return result;
}
@Atomic
public void push(int value) {
Node newNode = new Node(value);
m_last.setNext(newNode);
m_last = newNode;
}
@Atomic(metainf = "elastic")
public boolean removeInt(int value) {
Node previous = m_first;
Node node = previous.getNext();
boolean found = false;
while (node != null) {
if (node.getValue() == value) {
found = true;
break;
}
previous = node;
node = previous.getNext();
}
if (found) {
Node next = node.getNext();
previous.setNext(next);
node.setNext(next);
// TODO: update m_last?
if (next == null) m_last = previous;
}
return found;
}
/**
* To respect the semantics of ConcurrentLinkedQueue
* returns true as soon as a modif occurs
*/
@Override
@Atomic(metainf = "elastic")
public boolean removeAll(Collection<Integer> C) {
boolean result = false;
for (int x : C) result |= this.removeInt(x);
return result;
}
@Atomic(metainf = "elastic")
public int pop() {
int value = m_first.getValue();
if (m_first == m_last) {
return -1; //queue is empty
}
m_first = m_first.getNext();
return value;
}
@Atomic(metainf = "elastic")
public boolean containsInt(int value) {
Node previous = m_first;
Node node = previous.getNext();
while (node != null) {
if (node.getValue() == value) {
return true;
}
previous = node;
node = previous.getNext();
}
return false;
}
@Atomic(metainf = "roregular")
public int size() {
int n = 0;
Node node = m_first;
while (node != null) {
n++;
node = node.getNext();
}
return n;
}
public class Node {
final private int m_value;
private Node m_next;
public Node(int value, Node next) {
m_value = value;
m_next = next;
}
public Node(int value) {
this(value, null);
}
public int getValue() {
return m_value;
}
public void setNext(Node next) {
m_next = next;
}
public Node getNext() {
return m_next;
}
}
/**
* This is called after the JVM warmup phase
* to make sure the data structure is well initalized.
* No need to do anything for this.
*
* Note the ugly hack to reset the init size of the queue after warmup
* otherwise queue size grow as fast as its add ops execute: they are
* always successful, as opposed to its remove.
*/
public void clear() {
m_last = m_first;
fill(Parameters.range, Parameters.size);
return;
}
@Atomic(metainf = "elastic")
public Object getInt(int value) {
Node previous = m_first;
Node node = previous.getNext();
while (node != null) {
if (node.getValue() == value) {
return node;
}
previous = node;
node = previous.getNext();
}
return null;
}
@Atomic(metainf = "regular")
public Object putIfAbsent(int x, int y) {
if (!containsInt(x)) addInt(y);
return null;
}
}