package net.floodlightcontroller.flowcache;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* PriorityPendingQueue class - This class is a variant implementation for PriorityBlockingQueue
* PriorityBlockingQueue implementation has two problems:
* 1. service for events with the same priority has no guarantee of FIFO sequence. This can be solved by override of comparator though.
* 2. PriorityBlockingQueue is implemented through heap, which has a O(log(n)) complexity for enqueue and dequeue operations.
* to get a O(1) complexity with enqueue and dequeue operations, we propose this PriorityPendingList class.
* <p>
* PriorityPendingQueue has three separate queues: High Priority, Medium Priority and Low Priority.
* the requirements here are:
* 1. dequeue from the Queue will always return the event with the highest priority
* 2. events with the same priority will be dequeued in their inserting order
* 3. enqueue and dequeue have O(1) complexity
*
* current only support offer() and take() methods
*
* @author meiyang
*
*/
public class PriorityPendingQueue<E> {
private LinkedBlockingQueue<E> highPriorityQueue;
private LinkedBlockingQueue<E> mediumPriorityQueue;
private LinkedBlockingQueue<E> lowPriorityQueue;
private final AtomicInteger count = new AtomicInteger(0);
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
private final int capacity;
public enum EventPriority {
HIGH,
MEDIUM,
LOW,
}
public PriorityPendingQueue() {
highPriorityQueue= new LinkedBlockingQueue<E>();
mediumPriorityQueue= new LinkedBlockingQueue<E>();
lowPriorityQueue= new LinkedBlockingQueue<E>();
capacity= Integer.MAX_VALUE;
}
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
try {
while (count.get() == 0)
notEmpty.await();
} catch (InterruptedException ie) {
notEmpty.signal(); // propagate to a non-interrupted thread
throw ie;
}
x = extract();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
x = extract();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E peek() {
//todo
return null;
}
public boolean offer(E e, EventPriority p) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
int c = -1;
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
insert(e,p);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
public boolean offer(E e) {
return false;
}
private E extract() {
E first = highPriorityQueue.poll();
if (first==null)
first = mediumPriorityQueue.poll();
if (first==null)
first = lowPriorityQueue.poll();
return first;
}
private boolean insert(E e, EventPriority p) {
boolean result = false;
switch (p) {
case HIGH:
result = highPriorityQueue.offer(e);
break;
case MEDIUM:
result = mediumPriorityQueue.offer(e);
break;
case LOW:
result = lowPriorityQueue.offer(e);
break;
}
return result;
}
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
private void fullyLock() {
putLock.lock();
takeLock.lock();
}
private void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
public int size() {
return count.get();
}
public void clear() {
fullyLock();
try {
highPriorityQueue.clear();
mediumPriorityQueue.clear();
lowPriorityQueue.clear();
count.set(0);
} finally {
fullyUnlock();
}
}
public boolean isEmpty() {
return count.get() == 0;
}
}