/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.mobicents.protocols.ss7.scheduler;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
*
* @author oifa yulian
*/
public class ConcurrentLinkedList<E> implements BlockingQueue<E> {
// main list
private Node<E> head = new Node<E>(null);
private Node<E> tail = new Node<E>(null);
// cached garbage items
private Node<E> cacheHead = new Node<E>(null);
private Node<E> cacheTail = new Node<E>(null);
private Node<E> tempNode;
private Object searchNode;
private Integer size = 0;
private Integer cacheSize = 0;
// notifiers
private Notifier notifierHead = new Notifier();
private Notifier notifierTail = new Notifier();
// cached garbage items
private Notifier notifierCacheHead = new Notifier();
private Notifier notifierCacheTail = new Notifier();
private Integer notifierSize = 0;
private Integer cacheNotifierSize = 0;
private ListIterator iterator = new ListIterator();
private Lock lock = new Lock();
public ConcurrentLinkedList() {
head.next = tail;
tail.previous = head;
cacheHead.next = cacheTail;
cacheTail.previous = cacheHead;
notifierHead.next = notifierTail;
notifierTail.previous = notifierHead;
notifierCacheHead.next = notifierCacheTail;
notifierCacheTail.previous = notifierCacheHead;
}
public boolean contains(Object o) {
if (o == null)
return false;
aquireAccess();
tempNode = head.next;
while (tempNode.element != null && !o.equals(tempNode.element))
tempNode = tempNode.next;
if (tempNode.element == null) {
releaseAccess();
return true;
}
releaseAccess();
return false;
}
public boolean containsAll(Collection<?> c) {
aquireAccess();
Iterator<? extends Object> e = c.iterator();
while (e.hasNext()) {
searchNode = e.next();
if (searchNode == null) {
releaseAccess();
return false;
}
tempNode = head.next;
while (tempNode.element != null && !searchNode.equals(tempNode.element))
tempNode = tempNode.next;
if (tempNode.element == null) {
releaseAccess();
return false;
}
}
releaseAccess();
return true;
}
public boolean add(E value) {
if (value == null)
return false;
aquireAccess();
tempNode = head.next;
while (tempNode.element != null && !value.equals(tempNode.element))
tempNode = tempNode.next;
if (tempNode.element != null) {
releaseAccess();
return false;
}
if (cacheSize == 0)
// need new node
tempNode = new Node(value);
else {
// obtain node from cache decrease cache size
tempNode = cacheHead.next;
cacheHead.next = tempNode.next;
cacheHead.next.previous = cacheHead;
tempNode.element = value;
cacheSize--;
}
// connect to main queue
tail.previous.next = tempNode;
tempNode.previous = tail.previous;
tempNode.next = tail;
tail.previous = tempNode;
size++;
if (notifierSize > 0)
updateNotifier(value);
releaseAccess();
return true;
}
public boolean addAll(Collection<? extends E> c) {
Boolean result = false;
for (E item : c)
result |= add(item);
return result;
}
public void put(E o) {
offer(o);
}
public boolean offer(E o, long timeout, TimeUnit unit) {
return offer(o);
}
public boolean offer(E value) {
if (value == null)
return false;
aquireAccess();
if (cacheSize == 0)
// need new node
tempNode = new Node(value);
else {
// obtain node from cache decrease cache size
tempNode = cacheHead.next;
cacheHead.next = tempNode.next;
cacheHead.next.previous = cacheHead;
tempNode.element = value;
cacheSize--;
}
// connect to main queue
tail.previous.next = tempNode;
tempNode.previous = tail.previous;
tempNode.next = tail;
tail.previous = tempNode;
size++;
if (notifierSize > 0)
updateNotifier(value);
releaseAccess();
return true;
}
public E peek() {
aquireAccess();
E currValue = head.next.element;
releaseAccess();
return currValue;
}
public E element() throws NoSuchElementException {
aquireAccess();
if (size == 0) {
releaseAccess();
throw new NoSuchElementException();
}
E currValue = head.next.element;
releaseAccess();
return currValue;
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
aquireAccess();
tempNode = head.next;
int i = 0;
Object[] result = a;
while (tempNode.element != null && i < a.length) {
result[i++] = tempNode.element;
tempNode = tempNode.next;
}
releaseAccess();
while (i < a.length)
result[i] = null;
return a;
}
public Object[] toArray() {
aquireAccess();
Object[] result = new Object[size];
tempNode = head.next;
int i = 0;
while (i < size) {
result[i++] = tempNode.element;
tempNode = tempNode.next;
}
releaseAccess();
return result;
}
public E poll() {
aquireAccess();
if (size == 0) {
releaseAccess();
return null;
}
// disconnect from main list
tempNode = head.next;
head.next = tempNode.next;
head.next.previous = head;
// connect to cache list
tempNode.next = cacheHead.next;
cacheHead.next = tempNode;
tempNode.previous = cacheHead;
tempNode.next.previous = tempNode;
// update counters
E currValue = tempNode.element;
cacheSize++;
size--;
releaseAccess();
return currValue;
}
public E take() {
aquireAccess();
if (size == 0) {
Notifier<E> tempNotifier;
if (cacheNotifierSize == 0)
// need new node
tempNotifier = new Notifier();
else {
// obtain node from cache decrease cache size
tempNotifier = notifierCacheHead.next;
notifierCacheHead.next = tempNotifier.next;
notifierCacheHead.next.previous = notifierCacheHead;
cacheNotifierSize--;
}
// store it in notifiers list
// connect to main queue
notifierTail.previous.next = tempNotifier;
tempNotifier.previous = notifierTail.previous;
tempNotifier.next = notifierTail;
notifierTail.previous = tempNotifier;
notifierSize++;
while (size == 0) {
// release lock so others will be able to add nodes and wait
releaseAccess();
synchronized (tempNotifier.lock) {
try {
tempNotifier.lock.wait();
} catch (InterruptedException e) {
}
}
aquireAccess();
if (size != 0) {
// take back notifier
tempNotifier.previous.next = tempNotifier.next;
tempNotifier.next.previous = tempNotifier.previous;
tempNotifier.next = notifierCacheHead.next;
notifierCacheHead.next.previous = tempNotifier;
notifierCacheHead.next = tempNotifier;
tempNotifier.previous = notifierCacheHead;
cacheNotifierSize++;
}
}
}
// disconnect from main list
tempNode = head.next;
head.next = tempNode.next;
head.next.previous = head;
// connect to cache list
tempNode.next = cacheHead.next;
cacheHead.next = tempNode;
tempNode.previous = cacheHead;
tempNode.next.previous = tempNode;
// update counters
E currValue = tempNode.element;
cacheSize++;
size--;
releaseAccess();
return currValue;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
aquireAccess();
if (size == 0) {
Notifier<E> tempNotifier;
if (cacheNotifierSize == 0)
// need new node
tempNotifier = new Notifier();
else {
// obtain node from cache decrease cache size
tempNotifier = notifierCacheHead.next;
notifierCacheHead.next = tempNotifier.next;
notifierCacheHead.next.previous = notifierCacheHead;
cacheNotifierSize--;
}
// store it in notifiers list
// connect to main queue
notifierTail.previous.next = tempNotifier;
tempNotifier.previous = notifierTail.previous;
tempNotifier.next = notifierTail;
notifierTail.previous = tempNotifier;
notifierSize++;
while (size == 0) {
// release lock so others will be able to add nodes and wait
releaseAccess();
synchronized (tempNotifier.lock) {
try {
switch (unit) {
case MICROSECONDS:
tempNotifier.lock.wait(timeout / 1000L, (int) ((timeout * 1000L) % 1000000L));
break;
case MILLISECONDS:
tempNotifier.lock.wait(timeout);
break;
case NANOSECONDS:
tempNotifier.lock.wait(timeout / 1000000L, (int) (timeout % 1000000L));
break;
case SECONDS:
tempNotifier.lock.wait(timeout * 1000L);
break;
}
} catch (InterruptedException e) {
}
}
aquireAccess();
if (size == 0) {
releaseAccess();
throw new InterruptedException();
}
}
// take back notifier
tempNotifier.previous.next = tempNotifier.next;
tempNotifier.next.previous = tempNotifier.previous;
tempNotifier.next = notifierCacheHead.next;
notifierCacheHead.next.previous = tempNotifier;
notifierCacheHead.next = tempNotifier;
tempNotifier.previous = notifierCacheHead;
cacheNotifierSize++;
}
// disconnect from main list
tempNode = head.next;
head.next = tempNode.next;
head.next.previous = head;
// connect to cache list
tempNode.next = cacheHead.next;
cacheHead.next = tempNode;
tempNode.previous = cacheHead;
tempNode.next.previous = tempNode;
// update counters
E currValue = tempNode.element;
cacheSize++;
size--;
releaseAccess();
return currValue;
}
public E remove() throws NoSuchElementException {
aquireAccess();
if (size == 0) {
releaseAccess();
throw new NoSuchElementException();
}
// disconnect from main list
tempNode = head.next;
head.next = tempNode.next;
head.next.previous = head;
// connect to cache list
tempNode.next = cacheHead.next;
cacheHead.next = tempNode;
tempNode.previous = cacheHead;
tempNode.next.previous = tempNode;
// update counters
E currValue = tempNode.element;
cacheSize++;
size--;
releaseAccess();
return currValue;
}
public boolean remove(Object o) {
aquireAccess();
tempNode = head.next;
while (tempNode.element != null && !o.equals(tempNode.element))
tempNode = tempNode.next;
if (tempNode.element == null) {
releaseAccess();
return false;
}
tempNode.previous.next = tempNode.next;
tempNode.next.previous = tempNode.previous;
size--;
// return to cache
tempNode.next = cacheHead.next;
cacheHead.next = tempNode;
tempNode.previous = cacheHead;
tempNode.next.previous = tempNode;
releaseAccess();
return true;
}
public void clear() {
aquireAccess();
if (size > 0) {
cacheSize += size;
size = 0;
tail.previous.next = cacheHead.next;
cacheHead.next.previous = tail.previous;
cacheHead.next = head.next;
head.next.previous = cacheHead;
head.next = tail;
tail.previous = head;
}
releaseAccess();
}
public int drainTo(Collection<? super E> c) {
if (size == 0)
return 0;
if (c == null)
throw new NullPointerException();
if (c.equals(this))
throw new IllegalArgumentException();
aquireAccess();
Node<E> tempFirst = head.next;
Node<E> tempLast = tail.previous;
int tempCount = size;
head.next = tail;
tail.previous = head;
size = 0;
releaseAccess();
Node<E> tempNode = tempFirst;
for (int i = tempCount; i > 0; i--, tempNode = tempNode.next)
c.add(tempNode.element);
aquireAccess();
if (tempCount > 0) {
cacheSize += tempCount;
tempLast.next = cacheHead.next;
cacheHead.next.previous = tempLast;
cacheHead.next = tempFirst;
tempFirst.previous = cacheHead;
}
releaseAccess();
return tempCount;
}
public int drainTo(Collection<? super E> c, int maxElements) {
if (size == 0)
return 0;
if (c == null)
throw new NullPointerException();
if (c.equals(this))
throw new IllegalArgumentException();
aquireAccess();
tempNode = head.next;
int count = 0;
while (tempNode.element != null && count < maxElements) {
c.add(tempNode.element);
count++;
tempNode = tempNode.next;
}
size -= count;
if (count > 0) {
cacheSize += count;
Node<E> firstNode = head.next;
head.next = tempNode.next;
head.next.previous = head;
tempNode.next = cacheHead.next;
cacheHead.next.previous = tempNode;
cacheHead.next = firstNode;
firstNode.previous = cacheHead;
}
releaseAccess();
return count;
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public Iterator<E> iterator() {
iterator.currNode = head;
return iterator;
}
public int remainingCapacity() {
return Integer.MAX_VALUE;
}
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
private void updateNotifier(E newElement) {
if (notifierSize == 0)
return;
notifierSize--;
Notifier tempNotifier = notifierHead.next;
synchronized (tempNotifier.lock) {
tempNotifier.lock.notify();
}
}
private void aquireAccess() {
try {
lock.lock();
} catch (InterruptedException e) {
}
}
private void releaseAccess() {
lock.unlock();
}
private class Notifier<E> {
private Object lock = new Object();
Notifier next;
Notifier previous;
}
private class Node<E> {
E element;
Node<E> next;
Node<E> previous;
Node(E element) {
this.element = element;
}
}
private class ListIterator implements Iterator<E> {
private Node<E> currNode;
public ListIterator() {
this.currNode = head;
}
public boolean hasNext() {
return currNode.next.element != null;
}
public E next() {
aquireAccess();
E result = currNode.next.element;
currNode = currNode.next;
releaseAccess();
return result;
}
public void remove() {
aquireAccess();
tempNode = currNode;
currNode = currNode.next;
tempNode.previous.next = tempNode.next;
tempNode.next.previous = tempNode.previous;
size--;
// return to cache
tempNode.next = cacheHead.next;
cacheHead.next = tempNode;
tempNode.previous = cacheHead;
tempNode.next.previous = tempNode;
releaseAccess();
}
}
private class Lock {
protected boolean locked;
public Lock() {
locked = false;
}
public synchronized void lock() throws InterruptedException {
while (locked)
wait();
locked = true;
}
public synchronized void unlock() {
locked = false;
notify();
}
}
}